# Production-oriented topology (section 1/28): the host's own webserver # (cPanel/Apache or LiteSpeed -- already holding ports 80/443 for other # sites on the box) reverse-proxies to the API container on 127.0.0.1, # terminating TLS itself; there's no Traefik here. PgBouncer sits in front # of PostgreSQL, Redis and the worker are independently scaled. Requires a # populated `.env` (never commit real secrets). name: aurora-core networks: aurora-internal: driver: bridge services: postgres: image: postgres:17-alpine restart: unless-stopped env_file: .env environment: POSTGRES_USER: ${POSTGRES_USER:-aurora} POSTGRES_PASSWORD: ${POSTGRES_PASSWORD} POSTGRES_DB: ${POSTGRES_DB:-aurora_core} volumes: - aurora_postgres_data:/var/lib/postgresql/data networks: [aurora-internal] healthcheck: test: ["CMD-SHELL", "pg_isready -U ${POSTGRES_USER:-aurora}"] interval: 5s timeout: 5s retries: 10 deploy: resources: limits: { cpus: "2", memory: 2G } pgbouncer: image: edoburu/pgbouncer:latest restart: unless-stopped environment: DATABASE_URL: postgres://${POSTGRES_USER:-aurora}:${POSTGRES_PASSWORD}@postgres:5432/${POSTGRES_DB:-aurora_core} POOL_MODE: transaction MAX_CLIENT_CONN: "1000" DEFAULT_POOL_SIZE: "25" # postgres:17-alpine defaults password_encryption to scram-sha-256; # pgbouncer defaults auth_type to md5, which then fails every # connection with "wrong password type" against a scram-only server. AUTH_TYPE: scram-sha-256 depends_on: postgres: condition: service_healthy networks: [aurora-internal] healthcheck: test: ["CMD", "pg_isready", "-h", "localhost", "-p", "5432"] interval: 10s timeout: 5s retries: 10 redis: image: redis:7-alpine restart: unless-stopped command: ["redis-server", "--maxmemory", "512mb", "--maxmemory-policy", "allkeys-lru"] volumes: - aurora_redis_data:/data networks: [aurora-internal] healthcheck: test: ["CMD", "redis-cli", "ping"] interval: 5s timeout: 5s retries: 10 migrate: build: context: . dockerfile: docker/api.Dockerfile env_file: .env environment: DATABASE_URL_SYNC: postgresql+psycopg2://${POSTGRES_USER:-aurora}:${POSTGRES_PASSWORD}@postgres:5432/${POSTGRES_DB:-aurora_core} depends_on: postgres: condition: service_healthy networks: [aurora-internal] entrypoint: ["alembic"] command: ["upgrade", "head"] api: build: context: . dockerfile: docker/api.Dockerfile restart: unless-stopped env_file: .env environment: AURORA_ENV: production DATABASE_BEHIND_TRANSACTION_POOLER: "true" DATABASE_URL: postgresql+asyncpg://${POSTGRES_USER:-aurora}:${POSTGRES_PASSWORD}@pgbouncer:5432/${POSTGRES_DB:-aurora_core} REDIS_URL: redis://redis:6379/0 MEDIA_ROOT: /app/media volumes: - aurora_media_data:/app/media depends_on: pgbouncer: condition: service_healthy redis: condition: service_healthy migrate: condition: service_completed_successfully networks: [aurora-internal] # Bound to localhost only -- the host's own webserver (cPanel/Apache or # LiteSpeed, already holding 80/443 for other sites on the box) terminates # TLS for api.amdbs.net and reverse-proxies here. No Traefik on this box. ports: - "127.0.0.1:${AURORA_API_PORT:-8000}:8000" deploy: resources: limits: { cpus: "1", memory: 1G } worker: build: context: . dockerfile: docker/worker.Dockerfile restart: unless-stopped env_file: .env environment: AURORA_ENV: production DATABASE_BEHIND_TRANSACTION_POOLER: "true" DATABASE_URL: postgresql+asyncpg://${POSTGRES_USER:-aurora}:${POSTGRES_PASSWORD}@pgbouncer:5432/${POSTGRES_DB:-aurora_core} REDIS_URL: redis://redis:6379/0 MEDIA_ROOT: /app/media volumes: - aurora_media_data:/app/media depends_on: pgbouncer: condition: service_healthy redis: condition: service_healthy migrate: condition: service_completed_successfully networks: [aurora-internal] deploy: replicas: 2 resources: limits: { cpus: "1", memory: 1G } volumes: aurora_postgres_data: aurora_redis_data: aurora_media_data: