Docker Compose in Produktion: Healthcheck, Restart-Policy und Ressourcenlimits

Ein Server-Alarm weckt Sie mitten in der Nacht.
Am Rechner zeigt der Container-Status running – der grüne Punkt wirkt gesund. Doch der Service? 502 Bad Gateway.
Der Datenbank-Container ist noch nicht bereit, aber der Anwendungs-Container verbindet sich schon. Verbindung fehlgeschlagen, Service down. Der Container läuft noch – der Dienst ist längst tot.
So sieht der Alltag aus, wenn man Docker Compose zum ersten Mal in Produktion einsetzt.
Läuft und läuft stabil sind zwei verschiedene Dinge. Dass docker-compose up alles startet, heißt nicht, dass es um drei Uhr morgens bei Speicherexplosion oder Prozessabsturz noch steht.
Die drei Säulen für Docker Compose in Produktion – Healthcheck, Restart-Policy und Ressourcenlimits – schließen genau diese Lücke. Dieser Artikel fasst die Konfiguration aus der Praxis zusammen, inklusive kopierbarer YAML-Vorlage – damit Container nicht nur laufen, sondern stabil laufen.
1. Healthcheck: Ist der Container wirklich einsatzbereit?
Der Status running in docker ps bedeutet nur: Der Container-Prozess lebt noch. Ob er Dienste zuverlässig bereitstellt? Unklar.
Der Healthcheck lässt Docker Ihren Container regelmäßig prüfen: HTTP-Request senden, Datenbank anpingen oder ein Skript ausführen – und so feststellen, ob der Service wirklich lebt.
Wie funktioniert der Healthcheck?
Docker sendet in dem von Ihnen gesetzten Intervall einen Prüfbefehl in den Container. Rückgabewert 0 bedeutet gesund, alles andere unhealthy. Nach mehreren Fehlversuchen wird der Container als unhealthy markiert.
Entscheidend: Ein fehlgeschlagener Healthcheck löst keinen Neustart aus. Er macht den Zustand nur sichtbar. Für automatische Wiederherstellung brauchen Sie depends_on mit Bedingungen und eine Restart-Policy.
Vier Parameter, die Sie kennen sollten
healthcheck:
test: ["CMD-SHELL", "curl -f http://localhost:3000/health || exit 1"]
interval: 30s # Wie oft prüfen
timeout: 10s # Timeout pro Prüfung
retries: 3 # Fehlversuche bis unhealthy
start_period: 60s # Toleranzphase – Fehler zählen nicht zu retries
Den Parameter start_period habe ich anfangs ignoriert – mit folgendem Ergebnis: Die App startet langsam, die DB-Verbindung braucht 40 Sekunden, aber der Healthcheck beginnt nach 10 Sekunden. Drei Fehlversuche – sofort unhealthy. Mit start_period: 60s bleibt genug Zeit für die Initialisierung.
Häufige Healthcheck-Befehle
Web-Service:
healthcheck:
test: ["CMD-SHELL", "curl -f http://localhost:3000/health || exit 1"]
interval: 30s
timeout: 10s
retries: 3
start_period: 30s
PostgreSQL:
healthcheck:
test: ["CMD-SHELL", "pg_isready -U postgres"]
interval: 10s
timeout: 5s
retries: 5
Redis:
healthcheck:
test: ["CMD", "redis-cli", "ping"]
interval: 10s
timeout: 3s
retries: 3
Mit depends_on abhängige Services bedingt starten
Hier wird der Healthcheck am nützlichsten: Der abhängige Service startet erst, wenn die Abhängigkeit wirklich bereit ist.
services:
app:
depends_on:
db:
condition: service_healthy # Warten bis DB-Healthcheck bestanden
redis:
condition: service_healthy # Warten bis Redis-Healthcheck bestanden
Früher nutzte ich depends_on: [db, redis] – die App startete, während die Datenbank noch initialisierte. Verbindung fehlgeschlagen, sofortiger Exit. Mit condition: service_healthy wartet die App, bis die DB auf pg_isready antwortet. Ruhe.
2. Restart-Policy: Self-Healing für Container
Container down – wer richtet ihn auf?
Manuell docker restart? Probieren Sie das um drei Uhr nachts bei einem Alarm.
Die Restart-Policy überlässt das dem Docker-Daemon. Beim Container-Exit entscheidet Docker automatisch, ob neu gestartet wird.
Vier Policies im Vergleich
| Policy | Verhalten | Einsatz |
|---|---|---|
no | Kein Neustart nach Exit | Temporäre Tests, CI/CD |
always | Neustart bei jedem Exit | Kerndienste |
on-failure | Neustart nur bei abnormalen Exits | Task-Container |
unless-stopped | Immer neu starten, außer manuell gestoppt | Produktion – Standard |
Produktion: unless-stopped
restart: unless-stopped
Warum unless-stopped statt always?
Der Unterschied liegt im Verhalten nach manuellem docker stop:
always: Nach manuellem Stop startet der Container bei System- oder Docker-Neustart wiederunless-stopped: Nach manuellem Stop bleibt er gestoppt – kein unerwartetes Wiederaufwachen
Stellen Sie sich vor: Sie stoppen einen Container für Wartung, und nach Server-Neustart läuft er von selbst wieder. Das will niemand.
on-failure mit Retry-Limit
on-failure erlaubt eine Begrenzung der Neustarts:
restart: on-failure:5 # Maximal 5 Neustarts
Schlägt der Start fünfmal hintereinander fehl, gibt Docker auf. Sinnvoll, wenn externe Ursachen (DB nicht erreichbar, Konfigurationsfehler) zu wiederholten Abstürzen führen – so vermeiden Sie Endlos-Neustart-Schleifen.
Kurz zur Auswahl
- Kerndienste (Web, API, Datenbank):
unless-stopped - Hintergrundjobs, Cron-Skripte:
on-failure - Entwicklung, temporäre Läufe:
no
Ein Fallstrick: Die Restart-Policy regelt nur, ob nach Container-Exit neu gestartet wird – nicht, ob der Dienst wirklich einsatzbereit ist. Dafür brauchen Sie den Healthcheck.
3. Ressourcenlimits (deploy.resources): Container unter Kontrolle halten
Kennen Sie das? Ein Container mit Speicherleck frisst den gesamten Server-Speicher – der OOM Killer beendet alle anderen Container.
Ich kenne es. Unangenehm.
Ressourcenlimits setzen pro Container eine Obergrenze: Wird sie überschritten, wird der Container beendet – andere Dienste bleiben geschützt.
limits vs. reservations
deploy:
resources:
limits:
cpus: '1.0' # Maximal 1 CPU
memory: 512M # Maximal 512 MB Speicher
reservations:
cpus: '0.5' # Mindestens 0,5 CPU reserviert
memory: 256M # Mindestens 256 MB reserviert
- limits: harte Grenze – Überschreitung beendet den Prozess (OOM)
- reservations: weiche Grenze – teilt dem Scheduler das Mindest-Ressourcenbudget mit
Kurz gesagt: limits = „nicht mehr als“, reservations = „mindestens garantiert“.
CPU-Limits setzen
cpus: '1.0' # Maximal 1 CPU-Kern
cpus: '0.5' # Maximal 50 % CPU
cpus: '2.0' # Maximal 2 Kerne
CPU-Limits sind weich – bei Überschreitung wird gedrosselt, nicht beendet. Lieber etwas großzügig setzen.
Speicher-Limits setzen
memory: 512M # 512 MB
memory: 2G # 2 GB
Speicher-Limits sind hart. Überschreitung – Container wird vom OOM Killer beendet, ohne Diskussion.
Meine Richtwerte:
- Node.js-Apps: mindestens
512M, in Produktion eher1G - Python-Apps:
256M – 512M - PostgreSQL: je nach Verbindungen und Datenvolumen
1G – 4G - Redis:
256M – 512M, als Cache ggf. mehr
Praxis-Konfiguration
services:
app:
image: myapp:latest
deploy:
resources:
limits:
cpus: '1.0'
memory: 1G
reservations:
cpus: '0.25'
memory: 256M
So nutzt die App maximal 1 CPU und 1 GB Speicher; Docker garantiert mindestens 0,25 CPU und 256 MB.
Hinweis: deploy ist primär für Docker Swarm gedacht. Bei Einzelhost-Deployment mit docker-compose up greifen Ressourcenlimits ab Docker Compose V2 oder mit docker-compose --compatibility. Alternativ die veralteten Parameter mem_limit und cpus – oder direkt deploy (Compose V2.20+).
4. Vollständige Vorlage: produktionsreife YAML zum Kopieren
Jede Säule allein hilft – kombiniert ergibt sich produktionsreife Konfiguration. Beispiel für Web-Service + PostgreSQL + Redis, direkt anpassbar.
Vollständiges Beispiel
version: '3.8'
services:
# Web-Anwendung
app:
image: myapp:latest
restart: unless-stopped
depends_on:
db:
condition: service_healthy
redis:
condition: service_healthy
healthcheck:
test: ["CMD-SHELL", "curl -f http://localhost:3000/health || exit 1"]
interval: 30s
timeout: 10s
retries: 3
start_period: 60s
deploy:
resources:
limits:
cpus: '1.0'
memory: 1G
reservations:
cpus: '0.25'
memory: 256M
logging:
driver: json-file
options:
max-size: "10m" # Max. 10 MB pro Logdatei
max-file: "3" # Max. 3 Logdateien
# PostgreSQL-Datenbank
db:
image: postgres:15
restart: unless-stopped
environment:
POSTGRES_USER: appuser
POSTGRES_PASSWORD: apppassword
POSTGRES_DB: appdb
healthcheck:
test: ["CMD-SHELL", "pg_isready -U postgres"]
interval: 10s
timeout: 5s
retries: 5
start_period: 30s
deploy:
resources:
limits:
cpus: '2.0'
memory: 2G
reservations:
cpus: '0.5'
memory: 512M
volumes:
- pgdata:/var/lib/postgresql/data
# Redis-Cache
redis:
image: redis:7-alpine
restart: unless-stopped
healthcheck:
test: ["CMD", "redis-cli", "ping"]
interval: 10s
timeout: 3s
retries: 3
deploy:
resources:
limits:
cpus: '0.5'
memory: 512M
reservations:
memory: 128M
volumes:
pgdata:
Konfiguration für Dev und Prod trennen
Entwicklung und Produktion unterscheiden sich meist. Zwei Dateien halten das sauber getrennt:
# compose.yaml – Entwicklung
version: '3.8'
services:
app:
build: .
ports:
- "3000:3000"
restart: "no" # In Dev kein Auto-Restart
# compose.production.yaml – Produktions-Override
version: '3.8'
services:
app:
image: myapp:v1.2.3 # Fertiges Image in Produktion
restart: unless-stopped
healthcheck:
test: ["CMD-SHELL", "curl -f http://localhost:3000/health || exit 1"]
interval: 30s
timeout: 10s
retries: 3
deploy:
resources:
limits:
memory: 1G
Produktion starten:
docker-compose -f compose.yaml -f compose.production.yaml up -d
Beide Dateien werden zusammengeführt; compose.production.yaml überschreibt compose.yaml.
Log-Management: Festplatte schützen
Der Standard-Treiber json-file wächst ohne Limit. Ohne Obergrenze ist die Festplatte nach Monaten voll.
logging:
driver: json-file
options:
max-size: "10m" # Max. 10 MB pro Datei
max-file: "3" # Max. 3 Dateien, insgesamt max. 30 MB
Pro Container maximal 30 MB Logs mit automatischer Rotation. Diese Konfiguration setze ich bei jedem Service – kein manuelles Aufräumen mehr nötig.
Fazit
Die Logik der drei Säulen:
Healthcheck erkennt Probleme → Restart-Policy stellt wieder her → Ressourcenlimits verhindern Kettenreaktionen
Damit kann Ihre Docker-Compose-Anwendung:
- Beim Start auf bereite Abhängigkeiten warten – statt blind loszulaufen
- Nach Absturz selbst wieder hochkommen – ohne dass Sie nachts aufstehen müssen
- Einen unkontrollierten Container nicht den ganzen Server mitreißen lassen
Prüfen Sie jetzt Ihre docker-compose.yml. Was fehlt? Ergänzen Sie es.
Wenn Sie Docker Compose noch nicht produktiv nutzen: Nehmen Sie diese drei Konfigurationen beim nächsten Deployment mit. Sie werden es nicht bereuen.
Docker-Compose-Produktion: Drei Säulen konfigurieren
Healthcheck, Restart-Policy und Ressourcenlimits für ein stabiles Produktions-Deployment hinzufügen
⏱️ Estimated time: 15 min
- 1
Step 1: Healthcheck konfigurieren
Für jeden Service einen healthcheck-Block hinzufügen:
• test: Prüfbefehl (curl, pg_isready, redis-cli ping usw.)
• interval: Prüfintervall (empfohlen 10–30s)
• timeout: Timeout pro Prüfung (empfohlen 5–10s)
• retries: Anzahl Fehlversuche (empfohlen 3–5)
• start_period: Start-Toleranzphase (je nach Startzeit 30–60s) - 2
Step 2: Abhängige Services bedingt starten
depends_on mit condition-Parameter nutzen:
• depends_on: [db] ersetzen durch depends_on: db: condition: service_healthy
• Sicherstellen, dass der abhängige Service einen Healthcheck hat
• Anwendung startet erst, wenn Abhängigkeiten wirklich bereit sind - 3
Step 3: Restart-Policy festlegen
Je nach Service-Typ die passende Policy wählen:
• Kerndienste (Web/API/Datenbank): restart: unless-stopped
• Hintergrundjobs/Cron-Skripte: restart: on-failure:5
• Entwicklung/Debug: restart: "no"
• always vermeiden (startet nach manuellem Stop unerwartet neu) - 4
Step 4: Ressourcenlimits setzen
In deploy.resources limits und reservations konfigurieren:
• limits: harte Grenze – Überschreitung führt zum OOM Killer
• reservations: weiche Grenze – von Docker garantiertes Minimum
• Node.js-Apps: limits.memory mindestens 512M
• Datenbank je nach Verbindungen und Datenvolumen 1G–4G - 5
Step 5: Log-Rotation konfigurieren
Verhindern, dass Logdateien die Festplatte füllen:
• logging.driver: json-file (Standard-Treiber)
• logging.options.max-size: "10m" (max. 10 MB pro Datei)
• logging.options.max-file: "3" (3 Dateien behalten)
• Gesamt-Obergrenze 30 MB mit automatischer Rotation
FAQ
Startet der Container nach fehlgeschlagenem Healthcheck automatisch neu?
Was ist der Unterschied zwischen unless-stopped und always?
Was ist der Unterschied zwischen limits und reservations?
Wozu dient der Parameter start_period?
Wie nutzt man unterschiedliche Konfigurationen für Entwicklung und Produktion?
• compose.yaml: Entwicklung (build: ., restart: "no")
• compose.production.yaml: Produktions-Override (image: xxx, restart: unless-stopped)
• Startbefehl: docker-compose -f compose.yaml -f compose.production.yaml up -d
• Die zweite Datei überschreibt die erste – saubere Trennung der Umgebungen
Kann man deploy.resources auf einem Einzelhost nutzen?
7 Min. Lesezeit · Veröffentlicht am: 24. Apr. 2026 · Aktualisiert am: 14. Juli 2026
Docker Praxisleitfaden
Wenn du über die Suche hier gelandet bist, kommst du am schnellsten weiter, indem du zum vorherigen oder nächsten Beitrag dieser Serie springst.
Vorheriger
Docker Compose in Produktion: Healthchecks, Restart-Strategien und Log-Verwaltung
Praxisleitfaden für Docker Compose in der Produktion: Healthcheck-Konfiguration, Restart-Strategien im Detail und ein vollständiges Log-Management-Konzept. Von Container-Zombie-Zuständen bis zur automatischen Wiederherstellung – praxisnahe Konfigurationen gegen volllaufende Festplatten.
Teil 11 von 38
Nächster
Docker Compose: PHP-Umgebung mit einem Klick – DNMP-Tutorial (Nginx+MySQL+PHP)
Schritt für Schritt DNMP (Docker+Nginx+MySQL+PHP) mit Docker Compose deployen – in 10 Minuten, einheitliche Team-Umgebungen, inklusive vollständiger Konfigurationsdateien und Troubleshooting-Tipps
Teil 13 von 38
Ähnliche Beiträge
Docker vs. virtuelle Maschine: Performance-Unterschiede und Szenario-Auswahl in 5 Minuten

Docker vs. virtuelle Maschine: Performance-Unterschiede und Szenario-Auswahl in 5 Minuten
Docker-Installation ohne Fallstricke (2025): Vollständige Lösungen von permission denied bis zum erfolgreichen Start


Kommentare
Melde dich mit GitHub an, um einen Kommentar zu hinterlassen