# Production overlay for Custom PHP Framework # Extends base docker-compose.yml with production optimizations version: '3.8' services: web: environment: - APP_ENV=production - PHP_IDE_CONFIG= # Remove IDE config in production ports: # Remove development ports, only expose HTTPS - "${APP_SSL_PORT:-443}:443/tcp" - "443:443/udp" volumes: # Read-only application code in production - ./:/var/www/html:ro - ./ssl:/var/www/ssl:ro healthcheck: test: ["CMD", "curl", "-f", "-k", "https://localhost/health"] interval: 30s timeout: 10s retries: 3 start_period: 30s restart: always logging: driver: "json-file" options: max-size: "10m" max-file: "3" labels: "service=nginx,environment=production" deploy: resources: limits: memory: 256M cpus: '0.5' reservations: memory: 128M cpus: '0.25' php: environment: APP_ENV: production APP_DEBUG: false PHP_IDE_CONFIG: "" # Remove IDE config XDEBUG_MODE: off # Disable Xdebug in production build: args: - ENV=production - COMPOSER_INSTALL_FLAGS=--no-dev --optimize-autoloader --classmap-authoritative user: "www-data:www-data" # Use www-data in production volumes: # Read-only application code - ./:/var/www/html:ro # Writable storage volumes - storage-data:/var/www/html/storage:rw - var-data:/var/www/html/var:rw # Composer cache (but less aggressive caching in prod) - composer-cache:/var/www/.composer/cache healthcheck: test: ["CMD", "php", "/var/www/html/public/health.php"] interval: 30s timeout: 10s retries: 3 start_period: 60s restart: always logging: driver: "json-file" options: max-size: "10m" max-file: "3" labels: "service=php,environment=production" deploy: resources: limits: memory: 512M cpus: '1.0' reservations: memory: 256M cpus: '0.5' db: environment: - MYSQL_ROOT_PASSWORD=${DB_ROOT_PASSWORD} - MYSQL_DATABASE=${DB_DATABASE} - MYSQL_USER=${DB_USERNAME} - MYSQL_PASSWORD=${DB_PASSWORD} ports: [] # No external ports in production volumes: - db_data:/var/lib/mysql # Production MySQL configuration - ./docker/mysql/conf.d/security.cnf:/etc/mysql/conf.d/security.cnf:ro healthcheck: test: ["CMD", "mariadb-admin", "ping", "-h", "127.0.0.1", "-u", "root", "-p${DB_ROOT_PASSWORD}"] interval: 30s timeout: 10s retries: 5 start_period: 60s restart: always logging: driver: "json-file" options: max-size: "10m" max-file: "3" labels: "service=mariadb,environment=production" deploy: resources: limits: memory: 1G cpus: '1.0' reservations: memory: 512M cpus: '0.5' redis: volumes: # Use secure Redis configuration in production - ./docker/redis/redis-secure.conf:/usr/local/etc/redis/redis.conf:ro - redis_data:/data command: ["redis-server", "/usr/local/etc/redis/redis.conf"] healthcheck: test: ["CMD", "redis-cli", "ping"] interval: 30s timeout: 5s retries: 3 start_period: 30s restart: always logging: driver: "json-file" options: max-size: "10m" max-file: "3" labels: "service=redis,environment=production" deploy: resources: limits: memory: 256M cpus: '0.5' reservations: memory: 128M cpus: '0.25' queue-worker: environment: - APP_ENV=production - APP_DEBUG=false - WORKER_DEBUG=false - WORKER_SLEEP_TIME=${WORKER_SLEEP_TIME:-100000} - WORKER_MAX_JOBS=${WORKER_MAX_JOBS:-1000} - WORKER_MEMORY_LIMIT=${WORKER_MEMORY_LIMIT:-384M} build: args: - ENV=production - COMPOSER_INSTALL_FLAGS=--no-dev --optimize-autoloader user: "www-data:www-data" volumes: # Read-only application code - ./:/var/www/html:ro # Writable logs and storage - ./storage/logs:/var/www/html/storage/logs:rw - ./src/Framework/CommandBus/storage:/var/www/html/src/Framework/CommandBus/storage:rw restart: always stop_grace_period: 60s # Longer grace period for production logging: driver: "json-file" options: max-size: "10m" max-file: "3" labels: "service=queue-worker,environment=production" deploy: resources: limits: memory: 512M cpus: '0.5' reservations: memory: 256M cpus: '0.25' # Security-focused network configuration networks: frontend: driver: bridge ipam: driver: default config: - subnet: 172.24.0.0/24 backend: driver: bridge internal: true # Backend network is internal-only ipam: driver: default config: - subnet: 172.25.0.0/24 cache: driver: bridge internal: true # Cache network is internal-only ipam: driver: default config: - subnet: 172.26.0.0/24 volumes: redis_data: driver: local composer-cache: driver: local storage-data: driver: local var-data: driver: local db_data: driver: local