# Production-oriented topology (section 1/28): Traefik -> N stateless API # replicas -> PgBouncer -> PostgreSQL, plus Redis and independently-scaled # workers. Requires a pre-existing external Traefik network named # `traefik-public` and a populated `.env` (never commit real secrets). name: aurora-core networks: traefik-public: external: true 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" 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 depends_on: pgbouncer: condition: service_healthy redis: condition: service_healthy migrate: condition: service_completed_successfully networks: [traefik-public, aurora-internal] deploy: replicas: 3 resources: limits: { cpus: "1", memory: 1G } labels: - "traefik.enable=true" - "traefik.docker.network=traefik-public" - "traefik.http.routers.aurora-core-api.rule=Host(`${AURORA_API_HOST:-api.aurora.local}`)" - "traefik.http.routers.aurora-core-api.entrypoints=websecure" - "traefik.http.routers.aurora-core-api.tls.certresolver=letsencrypt" - "traefik.http.services.aurora-core-api.loadbalancer.server.port=8000" - "traefik.http.services.aurora-core-api.loadbalancer.healthcheck.path=/health" 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 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: