# Docker Compose Production Configuration **Stand:** 2025-11-07 **Status:** Vollständige Dokumentation der Production-Konfiguration --- ## Übersicht Dieses Dokument erklärt die Production-spezifische Docker Compose Konfiguration (`docker-compose.production.yml`). Diese Datei überschreibt die Basis-Konfiguration (`docker-compose.base.yml`) mit Production-spezifischen Einstellungen. **📖 Verwandte Dokumentation:** - [Application Stack Deployment](./application-stack.md) - Detaillierter Deployment-Ablauf - [Initial Deployment Guide](../guides/initial-deployment-guide.md) - Schritt-für-Schritt Anleitung - [Troubleshooting Guide](../troubleshooting/initial-deployment-issues.md) - Häufige Probleme und Lösungen --- ## Verwendung ```bash # Production Stack starten docker compose -f docker-compose.base.yml -f docker-compose.production.yml up -d # Mit .env Datei docker compose -f docker-compose.base.yml -f docker-compose.production.yml --env-file .env up -d ``` **Wichtig:** Die Production-Konfiguration wird immer zusammen mit der Basis-Konfiguration verwendet. --- ## Security Settings ### Capabilities **Web Container:** ```yaml cap_drop: - ALL cap_add: - CHOWN - DAC_OVERRIDE - NET_BIND_SERVICE # Required for binding to ports 80/443 - SETGID # Required for PHP-FPM to switch to www-data - SETUID # Required for PHP-FPM to switch to www-data ``` **PHP Container:** ```yaml cap_drop: - ALL cap_add: - CHOWN - DAC_OVERRIDE ``` **Wichtig:** `SETGID` und `SETUID` sind für den `web` Container erforderlich, damit PHP-FPM zum `www-data` User (UID 33) wechseln kann. **Trade-off:** `no-new-privileges: true` ist für den `web` Container **deaktiviert**, um PHP-FPM User Switching zu ermöglichen. Dies ist ein bewusster Security Trade-off. ### no-new-privileges **Web Container:** ```yaml # security_opt: # - no-new-privileges:true # ← Kommentiert ``` **PHP Container:** ```yaml security_opt: - no-new-privileges:true # ← Aktiv ``` **Grund:** Der `web` Container benötigt Privilege Escalation für PHP-FPM, während der `php` Container nach dem User Switch (`gosu`) keine weiteren Privileges benötigt. --- ## Environment Variables ### env_file vs. environment **env_file (Empfohlen):** ```yaml env_file: - /home/deploy/deployment/stacks/production/.env # ← Absoluter Pfad ``` **Wichtig:** **Immer absolute Pfade verwenden**, um sicherzustellen, dass die `.env` Datei gefunden wird, unabhängig vom Working Directory. **environment (Override):** ```yaml environment: - APP_ENV=production - APP_DEBUG=false - PHP_MEMORY_LIMIT=512M ``` **Reihenfolge:** `env_file` wird zuerst geladen, dann werden `environment` Variablen als Override angewendet. ### Docker Secrets Integration **Pattern:** `*_FILE` Environment Variables ```yaml environment: - DB_PASSWORD_FILE=/run/secrets/db_user_password - REDIS_PASSWORD_FILE=/run/secrets/redis_password - APP_KEY_FILE=/run/secrets/app_key - VAULT_ENCRYPTION_KEY_FILE=/run/secrets/vault_encryption_key secrets: - db_user_password - redis_password - app_key - vault_encryption_key ``` **Funktionsweise:** 1. Docker Secrets werden in `/run/secrets/` gemountet 2. Framework liest automatisch `*_FILE` Variablen und lädt den Inhalt 3. Secrets werden nie als Environment Variables exponiert **Siehe auch:** [Application Stack Deployment](./application-stack.md) - Secret Handling --- ## Entrypoint Overrides ### Queue Worker ```yaml queue-worker: entrypoint: "" # ← Leerer Entrypoint überschreibt Image Entrypoint command: ["php", "/var/www/html/worker.php"] ``` **Grund:** Der Image Entrypoint (`/usr/local/bin/entrypoint.sh`) startet PHP-FPM und Nginx. Der Queue Worker benötigt nur PHP CLI. ### Scheduler ```yaml scheduler: entrypoint: "" # ← Leerer Entrypoint überschreibt Image Entrypoint command: php console.php scheduler:run ``` **Grund:** Gleicher Grund wie Queue Worker - nur PHP CLI erforderlich. **Siehe auch:** [Troubleshooting Guide](../troubleshooting/initial-deployment-issues.md) - Container Entrypoint Override --- ## Health Checks ### Web Container ```yaml healthcheck: test: ["CMD", "curl", "-f", "https://localhost/health"] interval: 15s timeout: 5s retries: 5 start_period: 30s ``` **Funktionsweise:** - Prüft `/health` Endpoint via HTTPS - Startet nach 30 Sekunden (Container Startup Time) - Prüft alle 15 Sekunden - 5 Retries bei Fehlschlag ### PHP Container ```yaml healthcheck: test: ["CMD", "php", "-v"] interval: 15s timeout: 5s retries: 5 start_period: 30s ``` **Funktionsweise:** - Prüft PHP Verfügbarkeit - Einfacher Check für Container-Health ### Scheduler ```yaml healthcheck: test: ["CMD-SHELL", "php -r 'exit(0);' && test -f /var/www/html/console.php || exit 1"] interval: 60s timeout: 10s retries: 3 start_period: 30s ``` **Funktionsweise:** - Prüft PHP Verfügbarkeit und `console.php` Existenz - Längeres Interval (60s) da Scheduler weniger kritisch --- ## Volume Mounts ### Application Code ```yaml volumes: - /home/deploy/michaelschiemer/current:/var/www/html:rw ``` **Wichtig:** - **Absoluter Pfad** auf dem Host - **Read-Write** für `storage/` und `var/` Verzeichnisse - Code wird via `rsync` oder `git` synchronisiert **Siehe auch:** [Code Deployment Workflow](../guides/code-deployment-workflow.md) ### Certbot Volumes ```yaml volumes: - certbot-conf:/etc/letsencrypt:ro - certbot-www:/var/www/certbot:ro ``` **Funktionsweise:** - `certbot-conf`: SSL Zertifikate (Read-Only für Web Container) - `certbot-www`: Webroot für Let's Encrypt Challenge --- ## Resource Limits ### Web Container ```yaml deploy: resources: limits: memory: 512M cpus: '1.0' reservations: memory: 256M cpus: '0.5' ``` **Grund:** Nginx ist leichtgewichtig, benötigt wenig Ressourcen. ### PHP Container ```yaml deploy: resources: limits: memory: 1G cpus: '2.0' reservations: memory: 512M cpus: '1.0' ``` **Grund:** PHP-FPM benötigt mehr Ressourcen für Request-Verarbeitung. ### Queue Worker ```yaml deploy: resources: limits: memory: 2G cpus: '2.0' reservations: memory: 1G cpus: '1.0' ``` **Grund:** Queue Worker verarbeitet lange Jobs, benötigt mehr Memory. ### Scheduler ```yaml deploy: resources: limits: memory: 512M cpus: '0.5' reservations: memory: 256M cpus: '0.25' ``` **Grund:** Scheduler läuft periodisch, benötigt wenig Ressourcen. --- ## Logging ### JSON Logging mit Rotation ```yaml logging: driver: json-file options: max-size: "10m" max-file: "5" compress: "true" labels: "service,environment" ``` **Funktionsweise:** - **max-size:** Maximale Größe pro Log-Datei - **max-file:** Anzahl der Log-Dateien (Rotation) - **compress:** Komprimierung alter Log-Dateien - **labels:** Metadaten für Log-Aggregation **Beispiel:** Bei 5 Dateien à 10MB = max. 50MB Log-Speicher pro Container. --- ## Restart Policies ```yaml restart: always ``` **Funktionsweise:** - Container wird automatisch neu gestartet bei: - Container Exit (auch bei Fehlern) - Docker Daemon Restart - System Reboot **Alternative Policies:** - `no`: Kein automatischer Restart - `on-failure`: Nur bei Fehlern - `unless-stopped`: Immer, außer manuell gestoppt --- ## Dependencies ### Service Dependencies ```yaml depends_on: php: condition: service_healthy certbot: condition: service_started ``` **Funktionsweise:** - `service_healthy`: Wartet auf erfolgreichen Health Check - `service_started`: Wartet nur auf Container Start **Wichtig:** `queue-worker` und `scheduler` warten auf `php` Container Health, da sie die gleiche Codebase verwenden. --- ## External Services ### PostgreSQL ```yaml # Database service removed - using external PostgreSQL Stack # Connection via app-internal network using docker-compose.postgres-override.yml ``` **Grund:** PostgreSQL läuft in separatem Stack (`deployment/stacks/postgresql/`) für bessere Isolation. ### Redis ```yaml # Redis service removed - using external Redis Stack # Connection via app-internal network using docker-compose.redis-override.yml ``` **Grund:** Redis läuft in separatem Stack (`deployment/stacks/redis/`) für bessere Isolation. **Verbindung:** Via `app-internal` Network (siehe `docker-compose.base.yml`). --- ## Networks ### Cache Network ```yaml networks: cache: internal: true # Cache network is internal in production ``` **Funktionsweise:** - **Internal:** Keine externe Verbindung möglich - Nur für Container-zu-Container Kommunikation - Erhöht Security (Redis nicht von außen erreichbar) --- ## Best Practices ### 1. Absolute Pfade für env_file **✅ Richtig:** ```yaml env_file: - /home/deploy/deployment/stacks/production/.env ``` **❌ Falsch:** ```yaml env_file: - .env # Relativer Pfad - funktioniert nur im richtigen Verzeichnis ``` ### 2. Entrypoint Override für CLI-Container **✅ Richtig:** ```yaml queue-worker: entrypoint: "" command: ["php", "/var/www/html/worker.php"] ``` **❌ Falsch:** ```yaml queue-worker: # Kein Entrypoint Override - startet PHP-FPM/Nginx unnötig ``` ### 3. Capabilities für PHP-FPM **✅ Richtig:** ```yaml web: cap_add: - SETGID - SETUID ``` **❌ Falsch:** ```yaml web: security_opt: - no-new-privileges:true # Verhindert PHP-FPM User Switching ``` --- ## Troubleshooting ### Problem: Container startet nicht / unhealthy **Lösung:** Siehe [Troubleshooting Guide](../troubleshooting/initial-deployment-issues.md) **Häufige Ursachen:** - Missing Environment Variables → `env_file` mit absolutem Pfad prüfen - PHP-FPM Permission Errors → Capabilities (SETGID/SETUID) prüfen - Missing Application Code → Code-Sync prüfen ### Problem: Environment Variables werden nicht geladen **Ursache:** Relativer Pfad in `env_file` **Lösung:** Absoluten Pfad verwenden: ```yaml env_file: - /home/deploy/deployment/stacks/production/.env ``` ### Problem: Queue Worker startet PHP-FPM **Ursache:** Entrypoint Override fehlt **Lösung:** ```yaml queue-worker: entrypoint: "" command: ["php", "/var/www/html/worker.php"] ``` --- ## Referenz - [Application Stack Deployment](./application-stack.md) - Detaillierter Ablauf - [Initial Deployment Guide](../guides/initial-deployment-guide.md) - Schritt-für-Schritt Anleitung - [Troubleshooting Guide](../troubleshooting/initial-deployment-issues.md) - Probleme und Lösungen - [Code Deployment Workflow](../guides/code-deployment-workflow.md) - Code-Synchronisation