# ©AngelaMos | 2026 # compose.yml # # Production Docker Compose stack for AngelusVigil # # Orchestrates 5 services on the vigil_network bridge: # postgres (18-alpine with healthcheck and persistent # volume), redis (7.4-alpine with custom redis.conf), # backend (FastAPI with asyncpg, Redis, nginx log tail, # GeoIP, and model data volumes), frontend (Vite # production build served via nginx on the host port), # and geoip-updater (MaxMind weekly refresh). Joins the # external certgames_net network and mounts the external # certgames_nginx_logs volume for real-time log access. # Connects to infra/docker/fastapi.prod, # infra/docker/vite.prod, infra/redis/redis.conf, # infra/nginx/vigil.conf services: postgres: image: postgres:18-alpine container_name: vigil-postgres environment: POSTGRES_DB: ${POSTGRES_DB:-angelusvigil} POSTGRES_USER: ${POSTGRES_USER:-vigil} POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:?POSTGRES_PASSWORD required} POSTGRES_INITDB_ARGS: "-E UTF8 --locale=C" volumes: - postgres_data:/var/lib/postgresql networks: - vigil_network healthcheck: test: ["CMD-SHELL", "pg_isready -U ${POSTGRES_USER:-vigil} -d ${POSTGRES_DB:-angelusvigil}"] interval: 10s timeout: 5s retries: 5 start_period: 30s restart: always redis: image: redis:7.4-alpine container_name: vigil-redis command: redis-server /usr/local/etc/redis/redis.conf volumes: - redis_data:/data - ./infra/redis/redis.conf:/usr/local/etc/redis/redis.conf:ro networks: - vigil_network healthcheck: test: ["CMD", "redis-cli", "ping"] interval: 10s timeout: 5s retries: 5 start_period: 5s restart: always backend: build: context: . dockerfile: infra/docker/fastapi.prod container_name: vigil-backend environment: ENV: ${ENV:-production} DEBUG: ${DEBUG:-false} LOG_LEVEL: ${LOG_LEVEL:-INFO} API_KEY: ${API_KEY:?API_KEY required} DATABASE_URL: postgresql+asyncpg://${POSTGRES_USER:-vigil}:${POSTGRES_PASSWORD}@postgres:5432/${POSTGRES_DB:-angelusvigil} REDIS_URL: ${REDIS_URL:-redis://redis:6379} NGINX_LOG_PATH: /var/log/nginx/access.log GEOIP_DB_PATH: /usr/share/GeoIP/GeoLite2-City.mmdb volumes: - nginx_logs:/var/log/nginx:ro - geoip_data:/usr/share/GeoIP:ro - model_data:/app/data/models expose: - "8000" depends_on: postgres: condition: service_healthy redis: condition: service_healthy networks: - vigil_network healthcheck: test: ["CMD", "curl", "-f", "http://localhost:8000/health"] interval: 30s timeout: 10s retries: 3 start_period: 180s restart: always frontend: build: context: . dockerfile: infra/docker/vite.prod container_name: vigil-frontend ports: - "${FRONTEND_HOST_PORT:-80}:80" depends_on: backend: condition: service_healthy networks: - vigil_network - certgames_net healthcheck: test: ["CMD", "wget", "-qO-", "http://localhost/health"] interval: 30s timeout: 5s retries: 3 start_period: 10s restart: always geoip-updater: image: maxmindinc/geoipupdate:latest container_name: vigil-geoip environment: GEOIPUPDATE_ACCOUNT_ID: ${GEOIP_ACCOUNT_ID:?GEOIP_ACCOUNT_ID required} GEOIPUPDATE_LICENSE_KEY: ${GEOIP_LICENSE_KEY:?GEOIP_LICENSE_KEY required} GEOIPUPDATE_EDITION_IDS: "GeoLite2-City" GEOIPUPDATE_FREQUENCY: "168" volumes: - geoip_data:/usr/share/GeoIP networks: - vigil_network restart: always networks: vigil_network: driver: bridge name: vigil_network certgames_net: external: true name: certgames_default volumes: postgres_data: name: vigil_postgres_data redis_data: name: vigil_redis_data geoip_data: name: vigil_geoip_data model_data: name: vigil_model_data nginx_logs: external: true name: certgames_nginx_logs