87 lines
3.6 KiB
YAML
87 lines
3.6 KiB
YAML
|
|
# Thermograph production stack: the FastAPI app plus its PostgreSQL 18 database.
|
||
|
|
#
|
||
|
|
# docker compose up -d --build # or: make up
|
||
|
|
#
|
||
|
|
# POSTGRES_PASSWORD must be set at `docker compose` time — compose reads it from
|
||
|
|
# the repo-root .env for local runs (copy .env.example -> .env), and in prod the
|
||
|
|
# systemd unit's EnvironmentFile=/etc/thermograph.env puts it in the environment
|
||
|
|
# so `docker compose up` can interpolate it. It is used BOTH to initialize the db
|
||
|
|
# container and to build the app's THERMOGRAPH_DATABASE_URL below.
|
||
|
|
|
||
|
|
services:
|
||
|
|
db:
|
||
|
|
# PostgreSQL 18 + pg_duckdb (deploy/db/Dockerfile.db, FROM pgduckdb/pgduckdb:18-…,
|
||
|
|
# which IS PostgreSQL 18). Lets the DB read the app's parquet climate cache
|
||
|
|
# directly, e.g. SELECT * FROM read_parquet('/parquet/cache/*.parquet'). The init
|
||
|
|
# script CREATE EXTENSIONs pg_duckdb on a fresh volume.
|
||
|
|
build:
|
||
|
|
context: .
|
||
|
|
dockerfile: deploy/db/Dockerfile.db
|
||
|
|
environment:
|
||
|
|
POSTGRES_USER: thermograph
|
||
|
|
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:?set POSTGRES_PASSWORD}
|
||
|
|
POSTGRES_DB: thermograph
|
||
|
|
volumes:
|
||
|
|
# Mount the volume at the PARENT of the data dir and let the PG18 image use
|
||
|
|
# its own versioned subdir (/var/lib/postgresql/18/docker). Pinning PGDATA
|
||
|
|
# directly at the mountpoint trips an initdb chmod on some Docker setups; the
|
||
|
|
# whole tree still persists on the named volume this way.
|
||
|
|
- pgdata:/var/lib/postgresql
|
||
|
|
# Read the app's live parquet cache (the same appdata volume the app writes,
|
||
|
|
# mounted read-only) — query it under /parquet/cache/*.parquet.
|
||
|
|
- appdata:/parquet:ro
|
||
|
|
- ./deploy/db/init:/docker-entrypoint-initdb.d
|
||
|
|
healthcheck:
|
||
|
|
test: ["CMD-SHELL", "pg_isready -U thermograph -d thermograph"]
|
||
|
|
interval: 5s
|
||
|
|
timeout: 5s
|
||
|
|
retries: 10
|
||
|
|
# Cap the DB at 2 CPUs. Compose v2 honors the top-level `cpus:`; the
|
||
|
|
# deploy.resources block is the Swarm-style equivalent, kept for parity.
|
||
|
|
cpus: 2.0
|
||
|
|
deploy:
|
||
|
|
resources:
|
||
|
|
limits:
|
||
|
|
cpus: "2.0"
|
||
|
|
restart: unless-stopped
|
||
|
|
# No host port on purpose: the app reaches Postgres as db:5432 on the
|
||
|
|
# compose network. Nothing outside the stack should touch the database.
|
||
|
|
|
||
|
|
app:
|
||
|
|
build: .
|
||
|
|
depends_on:
|
||
|
|
db:
|
||
|
|
condition: service_healthy
|
||
|
|
environment:
|
||
|
|
# Built from POSTGRES_PASSWORD; this `environment` value wins over anything
|
||
|
|
# in env_file, so the URL always matches the db container's password.
|
||
|
|
THERMOGRAPH_DATABASE_URL: postgresql+asyncpg://thermograph:${POSTGRES_PASSWORD}@db:5432/thermograph
|
||
|
|
THERMOGRAPH_BASE: /
|
||
|
|
PORT: 8137
|
||
|
|
WORKERS: 4
|
||
|
|
# One worker wins this lock and runs the subscription notifier / homepage
|
||
|
|
# sweep; it lives on the appdata volume so it's shared across workers.
|
||
|
|
THERMOGRAPH_SINGLETON_LOCK: /app/data/notifier.lock
|
||
|
|
# Prod secrets live in /etc/thermograph.env: POSTGRES_PASSWORD,
|
||
|
|
# THERMOGRAPH_AUTH_SECRET, THERMOGRAPH_VAPID_PRIVATE_KEY/_PUBLIC_KEY,
|
||
|
|
# THERMOGRAPH_COOKIE_SECURE=1, mail/Discord keys, ... (see
|
||
|
|
# deploy/thermograph.env.example). `required: false` so local `docker compose
|
||
|
|
# up` works without that file — it reads POSTGRES_PASSWORD from repo-root .env.
|
||
|
|
env_file:
|
||
|
|
- path: /etc/thermograph.env
|
||
|
|
required: false
|
||
|
|
volumes:
|
||
|
|
# Parquet cache, notifier.lock, homepage.json, vapid.json persist here.
|
||
|
|
- appdata:/app/data
|
||
|
|
- applogs:/app/logs
|
||
|
|
cpus: 4.0
|
||
|
|
ports:
|
||
|
|
# Loopback only — host Caddy terminates TLS and reverse-proxies to this.
|
||
|
|
- "127.0.0.1:8137:8137"
|
||
|
|
restart: unless-stopped
|
||
|
|
|
||
|
|
volumes:
|
||
|
|
pgdata: {}
|
||
|
|
appdata: {}
|
||
|
|
applogs: {}
|