# ═══════════════════════════════════════════════════════════════ # FLUX SRL — Docker Compose (Production) # Services: PostgreSQL 16 + Next.js App + Nginx # ═══════════════════════════════════════════════════════════════ services: # ── PostgreSQL Database ── postgres: image: postgres:16-alpine restart: always environment: POSTGRES_USER: ${DB_USER} POSTGRES_PASSWORD: ${DB_PASSWORD} POSTGRES_DB: ${DB_NAME} volumes: - pgdata:/var/lib/postgresql/data networks: - flux-net healthcheck: test: ["CMD-SHELL", "pg_isready -U ${DB_USER} -d ${DB_NAME}"] interval: 5s timeout: 5s retries: 5 command: - postgres - -c - shared_buffers=256MB - -c - effective_cache_size=1GB - -c - work_mem=16MB - -c - maintenance_work_mem=128MB - -c - max_connections=50 - -c - random_page_cost=1.1 # ── Next.js Application ── app: build: context: . dockerfile: Dockerfile args: # NEXT_PUBLIC_GA_ID must be available at build time (Next.js inlines # NEXT_PUBLIC_* into the client bundle). Sourced from .env on the host. NEXT_PUBLIC_GA_ID: ${NEXT_PUBLIC_GA_ID:-} restart: always depends_on: postgres: condition: service_healthy environment: DATABASE_URL: postgresql://${DB_USER}:${DB_PASSWORD}@postgres:5432/${DB_NAME}?schema=public OPENAI_API_KEY: ${OPENAI_API_KEY} SESSION_SECRET: ${SESSION_SECRET} NEXT_PUBLIC_APP_URL: ${NEXT_PUBLIC_APP_URL} SMTP_HOST: ${SMTP_HOST} SMTP_PORT: ${SMTP_PORT} SMTP_USER: ${SMTP_USER} SMTP_PASS: ${SMTP_PASS} SMTP_FROM: ${SMTP_FROM} SMTP_SECURE: ${SMTP_SECURE} NODE_ENV: production # Optional: REDIS_URL enables multi-instance rate limiting. Leave unset # for the current single-container deploy — the in-memory store is used. REDIS_URL: ${REDIS_URL:-} REDIS_TOKEN: ${REDIS_TOKEN:-} volumes: - ./public/footage:/app/public/footage - ./public/applications:/app/public/applications - ./public/cases:/app/public/cases - ./public/news:/app/public/news - ./public/parts:/app/public/parts - ./public/operations-inbox:/app/public/operations-inbox - ./public/branding:/app/public/branding - ./public/team:/app/public/team networks: - flux-net expose: - "3000" healthcheck: test: - CMD-SHELL - "node -e \"fetch('http://localhost:3000/api/health').then(r => process.exit(r.ok ? 0 : 1)).catch(() => process.exit(1))\"" interval: 30s timeout: 5s retries: 3 start_period: 40s # ── Nginx Reverse Proxy ── nginx: image: nginx:alpine restart: always ports: - "80:80" - "443:443" volumes: - ./nginx/nginx.conf:/etc/nginx/nginx.conf:ro - ./nginx/conf.d:/etc/nginx/conf.d:ro - ./certbot/conf:/etc/letsencrypt:ro - ./certbot/www:/var/www/certbot:ro - ./public/cases:/srv/cases:ro - ./public/applications:/srv/applications:ro - ./public/news:/srv/news:ro - ./public/parts:/srv/parts:ro - ./public/footage:/srv/footage:ro - ./public/operations-inbox:/srv/operations-inbox:ro - ./public/branding:/srv/branding:ro - ./public/team:/srv/team:ro depends_on: - app networks: - flux-net volumes: pgdata: networks: flux-net: driver: bridge