Finance-Accounts/ar-aging-app/docker-compose.prod.yml

80 lines
2.4 KiB
YAML

# Production stack: caddy (auto-HTTPS) -> web (nginx: SPA + /api proxy) -> backend + mysql.
#
# cp .env.example .env.production # fill the PRODUCTION section first
# docker compose --env-file .env.production -f docker-compose.prod.yml up -d --build
#
# --env-file is REQUIRED: the ${AR_DOMAIN} / ${MYSQL_ROOT_PASSWORD} references below are
# resolved from it (env_file: alone only feeds the containers, not this YAML).
#
# Sized for one 8 GB server (300-500 MB Excel parsing needs the RAM). Backend runs ONE
# worker by design — jobs and their progress live in-process. See deploy/DEPLOY.md.
services:
mysql:
image: mysql:8.4
restart: unless-stopped
env_file: .env.production # uses MYSQL_PASSWORD / MYSQL_DATABASE / MYSQL_USER
environment:
MYSQL_ROOT_PASSWORD: ${MYSQL_ROOT_PASSWORD:?set MYSQL_ROOT_PASSWORD in .env.production}
command:
- --innodb-buffer-pool-size=1G
- --max-allowed-packet=256M
volumes:
- mysql_data:/var/lib/mysql
healthcheck:
test: ["CMD", "mysqladmin", "ping", "-h", "localhost", "-p${MYSQL_ROOT_PASSWORD}"]
interval: 10s
timeout: 5s
retries: 12
# Not exposed to the host network — only the backend reaches it.
backend:
build: ./backend
restart: unless-stopped
env_file: .env.production
environment:
AR_DATA_DIR: /data
MYSQL_HOST: mysql
AR_DB_BACKEND: mysql
volumes:
- ar_data:/data
depends_on:
mysql:
condition: service_healthy
healthcheck:
test: ["CMD", "python", "-c",
"import urllib.request;urllib.request.urlopen('http://localhost:8000/api/health', timeout=5)"]
interval: 30s
timeout: 10s
retries: 3
web:
build:
context: ./frontend
target: prod
restart: unless-stopped
depends_on:
- backend
# Not exposed directly — caddy fronts it with TLS.
# TLS terminator: automatic Let's Encrypt certificates for AR_DOMAIN, renewed by itself.
# No certbot cron, no cert plumbing. Set AR_DOMAIN (and a DNS A record) and it works.
caddy:
image: caddy:2-alpine
restart: unless-stopped
ports:
- "80:80"
- "443:443"
environment:
AR_DOMAIN: ${AR_DOMAIN:?set AR_DOMAIN in .env.production}
command: caddy reverse-proxy --from "https://${AR_DOMAIN}" --to web:80
volumes:
- caddy_data:/data
- caddy_config:/config
volumes:
mysql_data:
ar_data:
caddy_data:
caddy_config: