Docker Compose in Produktion: Healthchecks, Restart-Strategien und Log-Verwaltung

Server-Alarme häufen sich auf dem Handy. Im Terminal: Festplatte bei 99 % – Container-Logs belegen 50 GB.
Das war noch nicht das Schlimmste. Letztes Jahr zeigte ein API-Container running, die Datenbankverbindung war aber längst weg – alle Requests lieferten 500. Drei Stunden Fehlersuche. Laut Last9-Studie kostet ein Container-Zombie-Zustand im Schnitt 3,2 Stunden Troubleshooting.
Viele Teams deployen Docker Compose in Produktion zum ersten Mal mit Port-Mapping und Volume-Mount – fertig. Kein Healthcheck, keine Log-Rotation, Restart-Strategie oft nur restart: always. Ergebnis: Container wirken aktiv, sind aber tot; Logs wachsen unkontrolliert; abstürzende Services starten endlos neu und fressen CPU und RAM.
Dieser Artikel erklärt die drei Kernkonfigurationen für die Produktion – Healthcheck, Restart-Strategie und Log-Verwaltung – mit Beispielen, Healthcheck-Befehlen für gängige Services, Troubleshooting-Schritten und einer kopierfertigen docker-compose.yml-Vorlage.
Healthcheck – damit Container wirklich leben
Status running heißt nicht, dass die Anwendung arbeitet. Keine DB-Verbindung, Port nicht offen, Prozess hängt – Docker merkt das nicht. Der Healthcheck ist ein Herzschlag-Monitor, der regelmäßig prüft, ob die App noch antwortet.
Konfigurationssyntax
In docker-compose.yml sieht ein healthcheck so aus:
healthcheck:
test: ["CMD-SHELL", "pg_isready -U postgres"]
interval: 10s # alle 10 Sekunden prüfen
timeout: 5s # maximal 5 Sekunden pro Prüfung warten
retries: 5 # erst nach 5 Fehlversuchen unhealthy
start_period: 30s # 30 Sekunden Warm-up nach Start
Die Parameter müssen zusammenpassen. timeout darf nicht größer als interval sein, sonst überlappen sich Prüfungen. start_period nicht weglassen – Datenbanken brauchen Startzeit; zu kurz, und der Check markiert den Container fälschlich als tot.
Healthcheck-Befehle für gängige Services
Je nach Service unterschiedliche Checks:
PostgreSQL
healthcheck:
test: ["CMD-SHELL", "pg_isready -U postgres -d mydb"]
interval: 10s
timeout: 5s
retries: 5
start_period: 30s
pg_isready ist das PostgreSQL-eigene Tool, ob die DB Verbindungen annimmt.
MySQL / MariaDB
healthcheck:
test: ["CMD-SHELL", "mysqladmin ping -h localhost -u root -p$$MYSQL_ROOT_PASSWORD"]
interval: 10s
timeout: 5s
retries: 5
start_period: 30s
Passwort mit $$ escapen, sonst interpretiert YAML $ als Variable.
Redis
healthcheck:
test: ["CMD-SHELL", "redis-cli ping | grep PONG"]
interval: 10s
timeout: 3s
retries: 3
Redis ping liefert PONG – mit grep das Ergebnis absichern.
Webserver (HTTP-Check)
healthcheck:
test: ["CMD-SHELL", "curl -f http://localhost:8080/health || exit 1"]
interval: 30s
timeout: 10s
retries: 3
start_period: 10s
-f sorgt dafür, dass curl bei HTTP-Status ≠ 2xx mit Fehlercode endet und der Healthcheck fehlschlägt.
Fallstrick: In schlanken Images wie Alpine fehlt oft curl. Entweder installieren (apk add curl) oder wget nutzen:
test: ["CMD-SHELL", "wget --no-verbose --tries=1 --spider http://localhost:8080/health || exit 1"]
Startreihenfolge steuern
Die API startet, bevor die Datenbank bereit ist – Verbindungsfehler, Crash. Das kennen viele. depends_on mit condition: service_healthy hilft:
services:
postgres:
image: postgres:16
healthcheck:
test: ["CMD-SHELL", "pg_isready -U postgres"]
interval: 10s
timeout: 5s
retries: 5
start_period: 30s
api:
build: ./api
depends_on:
postgres:
condition: service_healthy # API erst nach healthy-Postgres
Docker Compose startet api erst, wenn postgres healthy ist – kein „DB noch nicht da, API verbindet schon“.
Restart-Strategie – kontrollierte Wiederherstellung nach Fehlern
Container abgestürzt – automatisch neu starten klingt gut. Ist die Ursache nicht behoben, entsteht eine Endlosschleife: CPU, RAM und echte Fehler verschwinden im Rauschen.
Konfigurationssyntax
Restart-Policy im deploy-Block:
deploy:
restart_policy:
condition: on-failure # nur bei Fehler neu starten
delay: 5s # 5 Sekunden warten vor Neustart
max_attempts: 3 # maximal 3 Neustartversuche
window: 120s # innerhalb 120s stabil = erfolgreiche Wiederherstellung
condition hat drei Werte:
none: kein Neustarton-failure: nur bei abnormalen Exits (Exit-Code ≠ 0)any: immer neu starten
Empfehlung für Produktion
on-failure statt always.
Warum? restart: always startet in jedem Fall neu – Bug, DB down, falsche Config. Crash-Loop, Log-Flut, CPU-Verbrauch.
Mit on-failure und max_attempts stoppt es nach wenigen Versuchen. Ops sieht den finalen Ausfall und kann die Ursache beheben.
Parameter feintunen
delay: Pause vor Neustart. Zu kurz – Container nicht sauber beendet; zu lang – längere Ausfallzeit. 5–10 Sekunden sind ein guter Start.
window wird oft übersehen: Wie lange muss der Container nach Neustart stabil laufen, damit der Versuch zählt? Bei window: 120s und erneutem Crash innerhalb von 120s wird max_attempts nicht zurückgesetzt – kein falscher Erfolg nach einer Sekunde Laufzeit.
Zusammenspiel mit Healthcheck
Healthcheck und Restart-Policy arbeiten zusammen:
- Healthcheck scheitert
retries-mal → Containerunhealthy - Mit
restart_policyversucht Docker einen Neustart - Nach Neustart zählt der Healthcheck von vorn
- Besteht der Check – normal; scheitert er wieder – weiter bis
max_attemptserschöpft
So gibt es automatische Wiederherstellung mit Begrenzung gegen Endlos-Restarts.
Log-Verwaltung – Festplatte nicht zum Platzen bringen
Der Alarm um drei Uhr morgens: 99 % Festplatte, 50 GB Logs – mehrfach erlebt. Der Standard-Treiber json-file rotiert nicht; ohne Limits wächst alles unbegrenzt.
Log-Rotation konfigurieren
In docker-compose.yml logging setzen:
logging:
driver: "json-file"
options:
max-size: "10m" # max. 10 MB pro Logdatei
max-file: "3" # höchstens 3 Dateien behalten
compress: "true" # alte Logs komprimieren
Maximal ~30 MB pro Container (10 MB × 3). Ab 10 MB neue Datei; ab vier Dateien wird die älteste gelöscht oder komprimiert.
Logs liegen unter /var/lib/docker/containers/<container-id>/<container-id>-json.log. Mit du prüfen:
du -sh /var/lib/docker/containers/*/*-json.log
Treiberwahl
Docker bietet json-file, syslog, fluentd, journald, local u. a. Für die meisten Fälle reichen json-file oder local.
Laut Docker-Dokumentation ist local effizienter als json-file und rotiert ohne manuelles max-size/max-file. Bei sehr hohem Logvolumen (zig GB pro Tag) local erwägen:
logging:
driver: "local"
Nachteil: docker logs funktioniert nicht direkt – ggf. mode: "non-blocking" in den Optionen.
Zentralisierte Log-Sammlung (optional)
Auf einem Host reichen json-file oder local. Bei vielen Servern und Containern hilft Zentralisierung:
- Fluentd: leichtgewichtig, kleine Cluster
- ELK Stack (Elasticsearch + Logstash + Kibana): mächtig, aufwändiger
- Loki + Grafana: cloud-nativ, gut zu Prometheus
Details zu Fluentd-Konfiguration – Kurzüberblick:
logging:
driver: "fluentd"
options:
fluentd-address: "localhost:24224"
tag: "docker.{{.Name}}"
Fluentd leitet Logs an die konfigurierte Adresse weiter.
Vollständige Konfigurationsvorlage
Healthcheck, Restart-Policy und Logging zusammen – produktionsreife docker-compose.yml mit PostgreSQL, Redis und API:
version: '3.8'
services:
postgres:
image: postgres:16
environment:
POSTGRES_USER: myuser
POSTGRES_PASSWORD: mypassword
POSTGRES_DB: mydb
volumes:
- postgres_data:/var/lib/postgresql/data
healthcheck:
test: ["CMD-SHELL", "pg_isready -U myuser -d mydb"]
interval: 10s
timeout: 5s
retries: 5
start_period: 30s
deploy:
restart_policy:
condition: on-failure
delay: 5s
max_attempts: 3
window: 120s
logging:
driver: "json-file"
options:
max-size: "10m"
max-file: "3"
compress: "true"
redis:
image: redis:7-alpine
healthcheck:
test: ["CMD-SHELL", "redis-cli ping | grep PONG"]
interval: 10s
timeout: 3s
retries: 3
start_period: 5s
deploy:
restart_policy:
condition: on-failure
delay: 5s
max_attempts: 3
logging:
driver: "json-file"
options:
max-size: "10m"
max-file: "3"
api:
build:
context: ./api
dockerfile: Dockerfile
ports:
- "8080:8080"
environment:
DATABASE_URL: postgres://myuser:mypassword@postgres:5432/mydb
REDIS_URL: redis://redis:6379
depends_on:
postgres:
condition: service_healthy
redis:
condition: service_healthy
healthcheck:
test: ["CMD-SHELL", "curl -f http://localhost:8080/health || exit 1"]
interval: 30s
timeout: 10s
retries: 3
start_period: 10s
deploy:
restart_policy:
condition: on-failure
delay: 10s
max_attempts: 3
window: 120s
logging:
driver: "json-file"
options:
max-size: "50m"
max-file: "5"
compress: "true"
volumes:
postgres_data:
Wichtige Punkte der Vorlage
Startreihenfolge: depends_on der API wartet auf healthy postgres und redis – keine Startfehler durch frühe Verbindungsversuche.
Loggrößen: postgres/redis meist 10 MB × 3; API oft mehr – 50 MB × 5. An echte Logmenge anpassen.
Restart-delay: postgres braucht nach Neustart Zeit – delay 5s; API delay 10s als Puffer für Healthchecks.
start_period: postgres 30s für DB-Init; redis 5s; API 10s für App-Start.
Vorlage kopieren, Umgebungsvariablen und Images anpassen. Weitere Services (MongoDB, MinIO) nach demselben Muster mit Healthcheck, Restart und Logging.
Häufige Fallstricke und Troubleshooting
Nach dem Deploy tauchen trotzdem Probleme auf – typische Fälle und Schritte.
Healthcheck schlägt dauerhaft fehl
Symptom: Status bleibt unhealthy, die App wirkt normal.
Schritte:
-
Tools im Check vorhanden?
docker exec <container> which curl docker exec <container> which pg_isreadyAlpine oft ohne curl – installieren oder wget.
-
Check-Befehl manuell:
docker exec <container> curl -f http://localhost:8080/healthFehler deuten auf den Health-Endpoint selbst.
-
Detaillierter Health-Status:
docker inspect --format='{{json .State.Health}}' <container> | jqLetzte Ergebnisse, Fehlerursachen, Zeitstempel.
Container startet ständig neu
Symptom: Start, kurzer Lauf, wieder down – Logs voller Restart-Einträge.
Schritte:
-
Exit-Grund:
docker inspect --format='{{.State.ExitCode}}' <container> docker inspect --format='{{.State.Error}}' <container>Exit-Codes: 1 = allgemeiner Fehler, 137 = OOM, 139 = Segfault.
-
Restart-Zähler:
docker inspect --format='{{.RestartCount}}' <container>Hohe Zahl – prüfen, ob
max_attemptsgreift. -
Logs:
docker logs --tail 100 <container>
Festplatte durch Logs voll
Symptom: Speicheralarm, /var/lib/docker/containers sehr groß.
Schritte:
-
Größte Logdateien:
du -sh /var/lib/docker/containers/*/*-json.log | sort -rh | head -5 -
Rotation aktiv?
docker inspect --format='{{.HostConfig.LogConfig}}' <container>Config: {}= keine Rotation konfiguriert. -
Logs manuell leeren (Notlösung):
truncate -s 0 /var/lib/docker/containers/<id>/<id>-json.logLangfristig Rotation in compose setzen.
Schnell-Befehle für die Fehlersuche
# Health-Status aller Container
docker ps --format "table {{.Names}}\t{{.Status}}"
# Healthcheck-Historie eines Containers
docker inspect --format='{{json .State.Health}}' <container>
# Exit-Code und Restart-Anzahl
docker inspect --format='ExitCode: {{.State.ExitCode}}, RestartCount: {{.RestartCount}}' <container>
# Logdateigrößen
du -sh /var/lib/docker/containers/*/*-json.log | sort -rh
# Letzte 100 Logzeilen
docker logs --tail 100 <container>
Fazit
In Produktion sind diese drei Konfigurationen Pflicht, nicht optional: Healthcheck zeigt, ob der Container wirklich lebt; Restart-Strategie ermöglicht Wiederherstellung mit Limit gegen Endlos-Loops; Log-Verwaltung schützt die Festplatte.
Kern-Konfigurationsliste:
- Healthcheck:
test+interval+timeout+retries+start_period - Restart-Strategie:
condition: on-failure+max_attempts: 3 - Log-Rotation:
max-size: 10m+max-file: 3+compress: true
Drei Schritte zum Handeln:
- Bestehende docker-compose.yml prüfen – fehlen die drei Blöcke, mindestens Healthcheck und Log-Rotation ergänzen.
- Mit der Vorlage einen Test-Service deployen – Healthcheck und Rotation beobachten.
- Troubleshooting-Befehle griffbereit halten – beim nächsten Nacht-Alarm schneller reagieren.
Lassen Sie Container in Produktion nicht ungeschützt laufen. Mit diesen drei Schutzebenen gibt es Auto-Recovery, schnellere Diagnose – und keine volllaufende Festplatte durch Logs.
FAQ
Wie setzt man interval und timeout für Healthchecks sinnvoll?
restart: always oder on-failure – was ist besser?
Wie groß sollten Logdateien sein und wie viele behalten?
Healthcheck schlägt dauerhaft fehl, die App läuft aber normal – was tun?
Wozu dient depends_on mit condition: service_healthy?
Wie prüft man schnell, wie viel Speicher Container-Logs belegen?
8 Min. Lesezeit · Veröffentlicht am: 12. 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 Fehlerbehebung: 5 häufige Fehler und schnelle Lösungen
Docker Compose schlägt fehl? Dieser Artikel fasst 5 häufige Fehlerklassen zusammen – Portkonflikte, Netzwerkprobleme, Build-Fehler, Container-Exit und Berechtigungsfehler – mit systematischem Troubleshooting-Workflow und mehreren Lösungsansätzen. So finden Sie das Problem in 5 Minuten.
Teil 10 von 38
Nächster
Docker Compose in Produktion: Healthcheck, Restart-Policy und Ressourcenlimits
Die drei Säulen für stabiles Docker-Compose-Deployment: Healthchecks für echte Bereitschaft, Restart-Policies für Self-Healing und Ressourcenlimits gegen unkontrolliertes Wachstum – inklusive vollständiger YAML-Vorlage.
Teil 12 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