# 🚀 Production Docker Compose # Complete production setup with all services containerized version: '3.8' services: # Frontend - Next.js Portal frontend: build: context: . dockerfile: apps/portal/Dockerfile target: production container_name: portal-frontend ports: - "3000:3000" environment: - NODE_ENV=production - PORT=3000 - HOSTNAME=0.0.0.0 - NEXT_PUBLIC_API_BASE=${NEXT_PUBLIC_API_BASE:-http://localhost:4000} - NEXT_PUBLIC_APP_NAME=${NEXT_PUBLIC_APP_NAME:-Customer Portal} restart: unless-stopped depends_on: backend: condition: service_healthy networks: - app-network healthcheck: test: ["CMD", "wget", "--no-verbose", "--tries=1", "--spider", "http://localhost:3000/api/health"] interval: 30s timeout: 10s retries: 3 start_period: 40s # Backend - NestJS BFF backend: build: context: . dockerfile: apps/bff/Dockerfile target: production container_name: portal-backend ports: - "4000:4000" environment: - NODE_ENV=production - PORT=4000 - DATABASE_URL=${DATABASE_URL} - REDIS_URL=${REDIS_URL} - JWT_SECRET=${JWT_SECRET} - JWT_EXPIRES_IN=${JWT_EXPIRES_IN:-7d} - BCRYPT_ROUNDS=${BCRYPT_ROUNDS:-12} - CORS_ORIGIN=${CORS_ORIGIN:-http://localhost:3000} - LOG_LEVEL=${LOG_LEVEL:-info} # External API configs - WHMCS_BASE_URL=${WHMCS_BASE_URL} - WHMCS_API_IDENTIFIER=${WHMCS_API_IDENTIFIER} - WHMCS_API_SECRET=${WHMCS_API_SECRET} - SF_LOGIN_URL=${SF_LOGIN_URL} - SF_CLIENT_ID=${SF_CLIENT_ID} - SF_PRIVATE_KEY_PATH=${SF_PRIVATE_KEY_PATH} - SF_USERNAME=${SF_USERNAME} volumes: - ./secrets:/app/secrets:ro restart: unless-stopped depends_on: database: condition: service_healthy cache: condition: service_healthy networks: - app-network healthcheck: test: ["CMD", "wget", "--no-verbose", "--tries=1", "--spider", "http://localhost:4000/health"] interval: 30s timeout: 10s retries: 3 start_period: 60s # PostgreSQL Database database: image: postgres:17-alpine container_name: portal-database environment: POSTGRES_DB: ${POSTGRES_DB:-portal_prod} POSTGRES_USER: ${POSTGRES_USER:-portal} POSTGRES_PASSWORD: ${POSTGRES_PASSWORD} volumes: - postgres_data:/var/lib/postgresql/data restart: unless-stopped networks: - app-network healthcheck: test: ["CMD-SHELL", "pg_isready -U ${POSTGRES_USER:-portal} -d ${POSTGRES_DB:-portal_prod}"] interval: 10s timeout: 5s retries: 5 start_period: 30s # Production PostgreSQL optimizations command: > postgres -c max_connections=100 -c shared_buffers=256MB -c effective_cache_size=1GB -c maintenance_work_mem=64MB -c checkpoint_completion_target=0.9 -c random_page_cost=1.1 -c effective_io_concurrency=200 # Redis Cache cache: image: redis:8-alpine container_name: portal-cache volumes: - redis_data:/data restart: unless-stopped networks: - app-network healthcheck: test: ["CMD", "redis-cli", "ping"] interval: 10s timeout: 3s retries: 5 # Production Redis optimizations command: > redis-server --maxmemory 256mb --maxmemory-policy allkeys-lru --save 900 1 --save 300 10 --save 60 10000 volumes: postgres_data: driver: local redis_data: driver: local networks: app-network: driver: bridge name: portal-prod-network