# Infrastructure services — long-lived, rarely restarted on deploy. # Managed separately from app services for zero-downtime deploys. # # Usage: docker compose -f docker-compose.infra.yml up -d services: postgres: image: pgvector/pgvector:pg17 container_name: sotto-prod-postgres restart: unless-stopped deploy: resources: limits: memory: 768m environment: POSTGRES_USER: ${POSTGRES_USER:-sotto} POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:?Set POSTGRES_PASSWORD in .env} POSTGRES_DB: ${POSTGRES_DB:-sotto} volumes: - sotto_postgres_data:/var/lib/postgresql/data command: - postgres - -c - shared_buffers=128MB - -c - effective_cache_size=512MB - -c - work_mem=8MB - -c - maintenance_work_mem=128MB - -c - max_connections=50 - -c - wal_buffers=16MB - -c - checkpoint_completion_target=0.9 - -c - random_page_cost=1.1 - -c - effective_io_concurrency=200 - -c - log_min_duration_statement=1000 - -c - log_lock_waits=on healthcheck: test: ['CMD-SHELL', 'pg_isready -h 127.0.0.1 -U ${POSTGRES_USER:-sotto}'] interval: 10s timeout: 5s retries: 5 networks: sotto-network: aliases: - postgres logging: driver: json-file options: max-size: '10m' max-file: '3' pgbouncer: image: edoburu/pgbouncer:v1.23.1-p3 container_name: sotto-prod-pgbouncer restart: unless-stopped deploy: resources: limits: memory: 64m environment: DATABASE_URL: postgresql://${POSTGRES_USER:-sotto}:${POSTGRES_PASSWORD}@postgres:5432/${POSTGRES_DB:-sotto} POOL_MODE: transaction DEFAULT_POOL_SIZE: 20 MAX_CLIENT_CONN: 200 MAX_DB_CONNECTIONS: 25 RESERVE_POOL_SIZE: 5 RESERVE_POOL_TIMEOUT: 3 SERVER_IDLE_TIMEOUT: 300 AUTH_TYPE: plain depends_on: postgres: condition: service_healthy healthcheck: test: ['CMD-SHELL', 'pg_isready -h 127.0.0.1 -p 5432 -U ${POSTGRES_USER:-sotto}'] interval: 10s timeout: 5s retries: 5 networks: sotto-network: aliases: - pgbouncer logging: driver: json-file options: max-size: '10m' max-file: '3' redis: image: redis:7-alpine container_name: sotto-prod-redis restart: unless-stopped deploy: resources: limits: memory: 384m command: >- redis-server --appendonly yes --maxmemory 256mb --maxmemory-policy noeviction --requirepass ${REDIS_PASSWORD:?Set REDIS_PASSWORD in .env} --maxclients 2000 --tcp-keepalive 60 volumes: - sotto_redis_data:/data healthcheck: test: ['CMD', 'redis-cli', '-a', '${REDIS_PASSWORD}', '--no-auth-warning', 'ping'] interval: 10s timeout: 5s retries: 5 networks: sotto-network: aliases: - redis logging: driver: json-file options: max-size: '10m' max-file: '3' pinchtab: image: pinchtab/pinchtab container_name: sotto-prod-pinchtab restart: unless-stopped deploy: resources: limits: memory: 2g environment: BRIDGE_BIND: '0.0.0.0' BRIDGE_PORT: '9867' BRIDGE_HEADLESS: 'true' BRIDGE_STEALTH: 'full' BRIDGE_BLOCK_IMAGES: 'true' BRIDGE_BLOCK_ADS: 'true' BRIDGE_BLOCK_MEDIA: 'true' BRIDGE_NAV_TIMEOUT: '25' shm_size: '1gb' security_opt: - seccomp:unconfined healthcheck: test: ['CMD-SHELL', 'wget -q --spider http://localhost:9867/health || exit 1'] interval: 30s timeout: 10s retries: 5 start_period: 30s networks: sotto-network: aliases: - pinchtab logging: driver: json-file options: max-size: '20m' max-file: '3' volumes: sotto_postgres_data: external: true name: sotto_postgres_data sotto_redis_data: external: true name: sotto_sotto_redis_data networks: sotto-network: name: sotto-network driver: bridge