Files
padelnomics/docker-compose.prod.yml
Deeman 76fc19c183 fix: litestream healthcheck gate + 1yr retention
Re-enable deploy gate on litestream: pgrep-based healthcheck with 6
retries (30s window) after a 15s start period — broken backups now
fail the deploy loudly instead of silently succeeding.

Extend retention from 7d to 1yr (8760h): WAL frames are tiny for a
low-traffic app, R2 free tier covers years of storage.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-02-20 13:00:29 +01:00

147 lines
3.8 KiB
YAML

services:
# ── Always-on infrastructure ──────────────────────────────
router:
image: nginx:alpine
restart: unless-stopped
ports:
- "5000:80"
volumes:
- ./router/default.conf:/etc/nginx/conf.d/default.conf:ro
networks:
- net
healthcheck:
test: ["CMD", "nginx", "-t"]
interval: 30s
timeout: 5s
litestream:
image: litestream/litestream:latest
restart: unless-stopped
# Auto-restore from R2 if DB file is missing, then start continuous replication.
# Handles: new server, deleted volume, disaster recovery.
entrypoint: /bin/sh
command:
- -c
- |
if [ ! -f /app/data/app.db ]; then
echo "==> No database found, restoring from R2..."
litestream restore -config /etc/litestream.yml /app/data/app.db \
|| echo "==> No backup found, starting fresh"
fi
exec litestream replicate -config /etc/litestream.yml
env_file: ./padelnomics/.env
volumes:
- app-data:/app/data
- ./padelnomics/litestream.yml:/etc/litestream.yml:ro
healthcheck:
test: ["CMD-SHELL", "pgrep -x litestream > /dev/null"]
interval: 5s
timeout: 5s
retries: 6
start_period: 15s
# ── Blue slot ─────────────────────────────────────────────
blue-app:
profiles: ["blue"]
build:
context: ./padelnomics
restart: unless-stopped
env_file: ./padelnomics/.env
environment:
- DATABASE_PATH=/app/data/app.db
volumes:
- app-data:/app/data
networks:
- net
healthcheck:
test: ["CMD", "python", "-c", "import urllib.request; urllib.request.urlopen('http://localhost:5000/health')"]
interval: 10s
timeout: 5s
retries: 3
start_period: 15s
blue-worker:
profiles: ["blue"]
build:
context: ./padelnomics
restart: unless-stopped
command: python -m padelnomics.worker
env_file: ./padelnomics/.env
environment:
- DATABASE_PATH=/app/data/app.db
volumes:
- app-data:/app/data
networks:
- net
blue-scheduler:
profiles: ["blue"]
build:
context: ./padelnomics
restart: unless-stopped
command: python -m padelnomics.worker scheduler
env_file: ./padelnomics/.env
environment:
- DATABASE_PATH=/app/data/app.db
volumes:
- app-data:/app/data
networks:
- net
# ── Green slot ────────────────────────────────────────────
green-app:
profiles: ["green"]
build:
context: ./padelnomics
restart: unless-stopped
env_file: ./padelnomics/.env
environment:
- DATABASE_PATH=/app/data/app.db
volumes:
- app-data:/app/data
networks:
- net
healthcheck:
test: ["CMD", "python", "-c", "import urllib.request; urllib.request.urlopen('http://localhost:5000/health')"]
interval: 10s
timeout: 5s
retries: 3
start_period: 15s
green-worker:
profiles: ["green"]
build:
context: ./padelnomics
restart: unless-stopped
command: python -m padelnomics.worker
env_file: ./padelnomics/.env
environment:
- DATABASE_PATH=/app/data/app.db
volumes:
- app-data:/app/data
networks:
- net
green-scheduler:
profiles: ["green"]
build:
context: ./padelnomics
restart: unless-stopped
command: python -m padelnomics.worker scheduler
env_file: ./padelnomics/.env
environment:
- DATABASE_PATH=/app/data/app.db
volumes:
- app-data:/app/data
networks:
- net
volumes:
app-data:
networks:
net: