# Production-specific Docker Compose overrides # Usage: docker-compose -f docker-compose.yml -f docker-compose.production.yml --env-file .env.production up -d # # This file overrides base configuration with production-specific settings: # - Stricter resource limits # - Production restart policies (always) # - JSON logging with proper rotation # - No host mounts (security) # - Internal networks (security) # - Production PostgreSQL configuration # - Certbot for SSL certificates version: '3.8' services: web: # Production restart policy restart: always # Production port mappings for Let's Encrypt ports: - "80:80" # HTTP for ACME challenge - "443:443" # HTTPS for production traffic # Override volumes - use Let's Encrypt certificates and mount application code volumes: - certbot-conf:/etc/letsencrypt:ro - certbot-www:/var/www/certbot:ro # Application code via rsync deployment - /home/deploy/michaelschiemer/current:/var/www/html:ro environment: - APP_ENV=production - APP_DEBUG=false # Stricter health checks for production healthcheck: test: ["CMD", "curl", "-f", "https://localhost/health"] interval: 15s timeout: 5s retries: 5 start_period: 30s # JSON logging with rotation logging: driver: json-file options: max-size: "10m" max-file: "5" compress: "true" labels: "service,environment" # Production resource limits (Nginx is lightweight) deploy: resources: limits: memory: 512M cpus: '1.0' reservations: memory: 256M cpus: '0.5' depends_on: php: condition: service_healthy certbot: condition: service_started # Networks must be explicitly defined to avoid override issues networks: - frontend - backend php: # Production restart policy restart: always # Override user setting - container must start as root for gosu to work # The entrypoint script will use gosu to switch to appuser after setup user: "root" # Override build args for production build: args: - ENV=production - COMPOSER_INSTALL_FLAGS=--no-dev --optimize-autoloader --classmap-authoritative environment: - APP_ENV=production - APP_DEBUG=false - PHP_MEMORY_LIMIT=512M - PHP_MAX_EXECUTION_TIME=30 # Disable Xdebug in production - XDEBUG_MODE=off # Stricter health checks healthcheck: test: ["CMD", "php", "-v"] interval: 15s timeout: 5s retries: 5 start_period: 30s # JSON logging logging: driver: json-file options: max-size: "10m" max-file: "10" compress: "true" labels: "service,environment" # Production resource limits deploy: resources: limits: memory: 1G cpus: '2.0' reservations: memory: 512M cpus: '1.0' # Production volumes volumes: # Mount application code from rsync deployment (read-only) - /home/deploy/michaelschiemer/current:/var/www/html:ro # Mount storage directory as writable volume (overlays the read-only code mount) - storage:/var/www/html/storage:rw # Mount .env file from shared directory (production environment variables) - /home/deploy/michaelschiemer/shared/.env.production:/var/www/html/.env:ro # Networks must be explicitly defined to avoid override issues networks: - backend - cache db: # Production restart policy restart: always # Use production PostgreSQL configuration volumes: - db_data:/var/lib/postgresql/data - ./docker/postgres/postgresql.production.conf:/etc/postgresql/postgresql.conf:ro - ./docker/postgres/init:/docker-entrypoint-initdb.d:ro # Production resource limits deploy: resources: limits: memory: 2G cpus: '2.0' reservations: memory: 1G cpus: '1.0' # Stricter health checks healthcheck: test: ["CMD-SHELL", "pg_isready -U ${DB_USERNAME:-postgres} -d ${DB_DATABASE:-michaelschiemer}"] interval: 10s timeout: 3s retries: 5 start_period: 30s # JSON logging logging: driver: json-file options: max-size: "20m" max-file: "10" compress: "true" labels: "service,environment" # Networks must be explicitly defined to avoid override issues networks: - backend redis: # Production restart policy restart: always # Production resource limits deploy: resources: limits: memory: 512M cpus: '1.0' reservations: memory: 256M cpus: '0.5' # Stricter health checks healthcheck: test: ["CMD", "redis-cli", "--raw", "incr", "ping"] interval: 10s timeout: 3s retries: 5 start_period: 10s # JSON logging logging: driver: json-file options: max-size: "10m" max-file: "5" compress: "true" labels: "service,environment" # Networks must be explicitly defined to avoid override issues networks: - cache queue-worker: # Use same image as php service (has application code copied) image: framework-production-php # Production restart policy restart: always # Override user setting - container must start as root for gosu to work # The entrypoint script will use gosu to switch to appuser after setup user: "root" # No entrypoint override - queue-worker runs worker.php directly # Worker command - direct PHP execution command: ["php", "/var/www/html/worker.php"] # Production volumes volumes: # Mount application code from rsync deployment (read-only) - /home/deploy/michaelschiemer/current:/var/www/html:ro # Mount storage directory as writable volume (overlays the read-only code mount) - storage:/var/www/html/storage:rw # Mount var directory as writable volume for cache and logs (overlays read-only code mount) - var-data:/var/www/html/var:rw # Mount .env file from shared directory (production environment variables) - /home/deploy/michaelschiemer/shared/.env.production:/var/www/html/.env:ro environment: - APP_ENV=production - WORKER_DEBUG=false - WORKER_SLEEP_TIME=100000 - WORKER_MAX_JOBS=10000 # Production resource limits deploy: resources: limits: memory: 2G cpus: '2.0' reservations: memory: 1G cpus: '1.0' # Note: replicas removed due to conflict with container_name # To scale queue workers, use separate docker-compose service definitions # JSON logging logging: driver: json-file options: max-size: "20m" max-file: "10" compress: "true" labels: "service,environment" # Graceful shutdown for long-running jobs stop_grace_period: 60s # Wait for dependencies to be healthy before starting depends_on: db: condition: service_healthy redis: condition: service_healthy php: condition: service_healthy # Networks must be explicitly defined to avoid override issues networks: - backend - cache # Certbot Sidecar Container for Let's Encrypt certbot: image: certbot/certbot:latest container_name: certbot restart: always volumes: # Share certificates with Nginx - certbot-conf:/etc/letsencrypt - certbot-www:/var/www/certbot # Logs for debugging - certbot-logs:/var/log/letsencrypt # Auto-renewal every 12 hours entrypoint: "/bin/sh -c 'trap exit TERM; while :; do certbot renew --webroot -w /var/www/certbot --quiet; sleep 12h & wait $${!}; done;'" networks: - frontend # JSON logging logging: driver: json-file options: max-size: "5m" max-file: "3" compress: "true" labels: "service,environment" networks: # Production networks with security isolation frontend: driver: bridge backend: driver: bridge # NOTE: backend must NOT be internal - PHP needs to communicate with DB! cache: driver: bridge internal: true # Cache network is internal volumes: # Let's Encrypt SSL Certificates certbot-conf: driver: local certbot-www: driver: local certbot-logs: driver: local # Application storage volume (single volume for entire storage directory) storage: driver: local # Database volume with backup driver (optional) db_data: driver: local # Optional: Use external volume for easier backups # driver_opts: # type: none # o: bind # device: /mnt/db-backups/michaelschiemer-prod