services: db: image: docker.io/library/postgres:17-alpine restart: unless-stopped env_file: .env environment: POSTGRES_INITDB_ARGS: "--encoding=UTF8 --no-locale" volumes: - pgdata:/var/lib/postgresql/data healthcheck: test: ["CMD-SHELL", "pg_isready -U $${POSTGRES_USER} -d $${POSTGRES_DB}"] interval: 5s timeout: 3s retries: 20 api: build: ./backend restart: unless-stopped env_file: .env command: ["sh", "-c", "alembic upgrade head && exec fintracker serve --host 0.0.0.0 --port 8000"] depends_on: db: condition: service_healthy volumes: - uploads:/data/uploads expose: - "8000" worker: build: ./backend restart: unless-stopped env_file: .env command: ["fintracker", "worker"] depends_on: db: condition: service_healthy volumes: - uploads:/data/uploads caddy: image: docker.io/library/caddy:2-alpine restart: unless-stopped env_file: .env ports: - "80:80" - "443:443" volumes: - ./deploy/Caddyfile:/etc/caddy/Caddyfile:ro - ./app/build/web:/srv/app:ro - caddy_data:/data - caddy_config:/config depends_on: - api pg-backup: image: docker.io/library/postgres:17-alpine restart: unless-stopped env_file: .env entrypoint: ["sh", "-c"] # nightly logical dump, keep 14 days; copy ./backups off-site with rclone/cron on the host command: - | while true; do ts=$(date +%F_%H%M) pg_dump -h db -U "$$POSTGRES_USER" -d "$$POSTGRES_DB" -Fc -f "/backups/fintracker_$$ts.dump" && \ find /backups -name 'fintracker_*.dump' -mtime +14 -delete sleep 86400 done environment: PGPASSWORD: ${POSTGRES_PASSWORD} volumes: - ./backups:/backups depends_on: db: condition: service_healthy volumes: pgdata: uploads: caddy_data: caddy_config: