Files
MealQuest/deploy/compose.yaml
T

101 lines
3.1 KiB
YAML
Raw Normal View History

# Production stack for MealQuest on 10.60.1.14 (D-018). Build on the host,
# then start through systemd:
2026-10-05 08:08:16 -04:00
#
# docker compose -f deploy/compose.yaml build
# sudo systemctl restart mealquest
#
# The services use restart: "no" on purpose: Docker must not start them
# before the DOCKER-USER firewall is applied. deploy/systemd/mealquest.service
# owns start and stop and requires mealquest-firewall.service first.
2026-10-05 08:08:16 -04:00
#
# Configuration comes from deploy/.env (copy deploy/.env.example) and secrets
# from deploy/secrets/; neither is committed. Compose bind-mounts file secrets
# with their host owner and mode, so each file in deploy/secrets/ must be
# owned by the container user 10001:10001 with mode 0600.
name: mealquest
services:
mealquest:
build:
context: ..
dockerfile: Dockerfile
user: "10001:10001"
restart: "no"
2026-10-05 08:08:16 -04:00
env_file:
- path: .env
required: true
# Fixed by this file's container shape (listen port, volume, secret
# mounts); deploy/.env must not set them.
environment:
MEALQUEST_HTTP_ADDR: ":8080"
MEALQUEST_DATA_DIR: /data
MEALQUEST_SECRET_KEY_FILE: /run/secrets/secret_key
MEALQUEST_OIDC_CLIENT_SECRET_FILE: /run/secrets/oidc_client_secret
secrets:
- secret_key
- oidc_client_secret
volumes:
- mealquest-data:/data
ports:
- "${MEALQUEST_BIND_ADDRESS:?set MEALQUEST_BIND_ADDRESS in deploy/.env}:8090:8080"
networks:
- mealquest
healthcheck:
test: ["CMD", "wget", "-q", "-O", "/dev/null", "http://127.0.0.1:8080/health"]
interval: 30s
timeout: 3s
start_period: 10s
retries: 3
security_opt:
- no-new-privileges:true
cap_drop:
- ALL
# Litestream streams the database to the Synology NAS over SFTP
# (config: deploy/litestream.yml). The SFTP private key
# deploy/secrets/litestream_key is a dedicated keypair, owned by
# 10001:10001 with mode 0600 like the other secrets.
#
# The data volume is mounted read-write on purpose: Litestream writes its
# tracking tables into the database and checkpoints the WAL. It runs as
# the app's own user (owner of the volume) with a read-only root
# filesystem, no privilege escalation and no Linux capabilities.
#
# Restore: follow "Restore the database" in deploy/RUNBOOK.md (stop the
# stack, move the database with its -wal and -shm aside, restore, check).
litestream:
image: litestream/litestream:0.5.17
user: "10001:10001"
restart: "no"
command: ["replicate", "-config", "/etc/litestream.yml"]
depends_on:
mealquest:
condition: service_healthy
volumes:
- mealquest-data:/data
- ./litestream.yml:/etc/litestream.yml:ro
- ./secrets/litestream_key:/etc/litestream/sftp_key:ro
networks:
- mealquest
read_only: true
tmpfs:
- /tmp
security_opt:
- no-new-privileges:true
cap_drop:
- ALL
2026-10-05 08:08:16 -04:00
secrets:
secret_key:
file: ./secrets/secret_key
oidc_client_secret:
file: ./secrets/oidc_client_secret
volumes:
mealquest-data:
name: mealquest-data
networks:
mealquest:
name: mealquest