Zum Inhalt springen
← Alle Beiträge
· 9 Min. Lesezeit· Von

Docker HEALTHCHECK und restart_policy: wann Container sich selbst heilen – und wann nicht

HEALTHCHECK-Optionen, restart_policy-Modi und depends_on: condition: service_healthy erklärt – inklusive Timing-Formel, einer verbatim-Fehlermeldung und dem Gotcha, das die meisten übersehen.

DockerDocker ComposeHealthcheckrestart_policyDevOpsSelf-HostingContainer

Ein Container mit Status running ist kein Beweis dafür, dass die Anwendung antwortet. Docker überwacht, ob der Hauptprozess noch läuft — nicht, ob er sinnvolle Antworten liefert. Eine Datenbank kann hochgefahren sein, aber noch keine Verbindungen akzeptieren. Eine API kann gestartet sein, aber auf ein fehlendes Secret warten. Mit HEALTHCHECK geben Sie Docker ein konkretes Prüfprogramm: Führe diesen Befehl aus, prüfe den Exit-Code, und ändere den Gesundheitszustand des Containers entsprechend. restart_policy entscheidet, was nach einem Absturz passiert — hat aber eine Einschränkung, die viele überrascht.

Was Docker ohne Healthcheck nicht sehen kann

running ≠ funktionsfähig

docker ps zeigt Up 3 minutes und Status: running. Das sagt über die Anwendung genau eines aus: Der Prozess läuft noch. Ein Node.js-Server, dessen Datenbankverbindung abgebrochen ist und der auf jeden Request mit 500 Internal Server Error antwortet, ist aus Sicht von Docker healthy — weil kein Healthcheck das Gegenteil feststellt. Ein PostgreSQL-Container, der noch im Initialisierungsmodus ist und keine Verbindungen annimmt, ist ebenfalls healthy. Das ist der blinde Fleck, und gerade bei abhängigen Services im selben Stack führt er zu schwer nachvollziehbaren Startfehlern.

Der blinde Fleck kostet im schlimmsten Fall bares Geld: Wer wissen möchte, was eine Stunde ungeplanter Ausfallzeit im eigenen Betrieb konkret bedeutet, kann das mit dem Ausfallkosten-Rechner schnell einschätzen.

Welche Services besonders von Healthchecks profitieren

Am stärksten fallen Healthchecks dort auf, wo andere Services auf einen Dienst warten: Datenbanken, Message Queues, Caches. Wer im Compose-Stack ein depends_on verwendet, bekommt ohne Healthcheck nur eine Startsequenz — nicht die Garantie, dass der Abhängigkeitsdienst betriebsbereit ist. Erst depends_on: condition: service_healthy liefert diese Garantie, und dafür braucht der Abhängigkeitsdienst einen konfigurierten Healthcheck. Auch für Services mit langsamer Initialisierung — JVM-Applikationsserver, Django-Migrationen, Elasticsearch-Cluster — lohnt ein sorgfältig eingestellter Healthcheck.

HEALTHCHECK schreiben: Dockerfile und Compose-Syntax im Vergleich

Optionen und ihre Standardwerte

Alle fünf Optionen gelten für Dockerfile und Compose-YAML gleichermaßen. Docker Engine 29.8.1 (Stand September 2026) verwendet folgende Standardwerte, die im Moby-Quellcode unter daemon/health.go definiert sind:

Option Standardwert Bedeutung
interval 30s Abstand zwischen zwei Prüfungen
timeout 30s Maximale Laufzeit des Prüfbefehls
retries 3 Aufeinanderfolgende Fehlschläge bis unhealthy
start_period 0s Anlaufzeit: Fehlschläge zählen in dieser Zeit nicht
start_interval 5s Prüfintervall während start_period (Docker ≥ 25.0)

Docker kennt drei Gesundheitszustände für einen Container: starting (noch in der Anlaufphase), healthy (letzte Prüfung erfolgreich) und unhealthy (zu viele aufeinanderfolgende Fehlschläge). Kein Healthcheck konfiguriert? Der Container hat gar keinen Status in diesem Sinne — docker inspect zeigt dann keinen Health-Schlüssel.

Dockerfile-Syntax:

HEALTHCHECK --interval=10s --timeout=5s --retries=5 \
            --start-period=30s --start-interval=5s \
  CMD pg_isready -U app -d appdb

Beachten Sie: Im Dockerfile werden Bindestriche verwendet (--start-period), in der Compose-YAML Unterstriche (start_period).

Compose-Syntax:

healthcheck:
  test: ["CMD-SHELL", "pg_isready -U app -d appdb"]
  interval: 10s
  timeout: 5s
  retries: 5
  start_period: 30s
  start_interval: 5s

CMD vs. CMD-SHELL: wann brauchen Sie eine Shell?

["CMD", "pg_isready", "-U", "app"] führt den Befehl direkt aus — kein Shell-Overhead, keine Variablenexpansion. Das ist die richtige Form, wenn das Werkzeug direkt aufgerufen werden kann. ["CMD-SHELL", "pg_isready -U app || exit 1"] führt den Ausdruck via /bin/sh -c aus und erlaubt Shell-Features wie ||, Pipes und Parametersubstitution.

Distroless- und Scratch-Images haben keine Shell. In diesen Images schlägt CMD-SHELL mit exec: "/bin/sh": stat /bin/sh: no such file or directory fehl. Wer sein Image nach dem Multi-Stage-Muster aufbaut und Werkzeuge bewusst aus dem Runtime-Image herausgelassen hat, sollte das im Hinterkopf behalten: Entweder eine statische Healthcheck-Binary mitkopieren oder einen datenbankspezifischen Befehl verwenden, der bereits im Image ist.

Ein häufiger Fehler ist curl --fail http://localhost/health als Healthcheck in einem Image, das kein curl enthält. Docker protokolliert dann in .State.Health.Log die folgende Fehlermeldung:

OCI runtime exec failed: exec failed: unable to start container process:
exec: "curl": executable file not found in $PATH

Nach --retries Fehlschlägen steht der Container auf unhealthy. Besser: wget -qO- http://localhost/health (häufiger in Alpine-Images vorhanden als curl), pg_isready für PostgreSQL, redis-cli PING für Redis.

start_period und start_interval: schnelles Polling beim Hochfahren

start_period definiert eine Anlaufzeit, in der Fehlschläge nicht gegen --retries gezählt werden — gedacht für Services mit langsamer Initialisierung: Datenbankmigrationen, JVM-Warmup, Elasticsearch-Cluster-Aufbau. start_interval erlaubt während dieser Anlaufzeit häufigeres Polling als im Normalbetrieb (eingeführt mit Docker Engine 25.0).

Wichtige Einschränkung: start_interval wird stillschweigend ignoriert, wenn start_period auf 0s steht — dem Standardwert. Wer start_interval: 5s setzt und kein start_period angibt, bekommt kein schnelleres Polling. Dieses Verhalten ist dokumentiert (moby/moby Issue #49900), steht aber nicht prominent in der offiziellen Prose-Dokumentation.

restart_policy: vier Modi mit einem entscheidenden Unterschied

Vergleichstabelle

Wert Startet nach Absturz neu? Startet nach docker compose down + Daemon-Neustart?
no (Standard) Nein Nein
on-failure Ja, bei Exit-Code ≠ 0 Nein
unless-stopped Ja Nur wenn kein manueller Stop vorausging
always Ja Immer — auch nach manuellem Stop

Warum unless-stopped für selbst gehostete Dienste die bessere Wahl ist

always und unless-stopped verhalten sich im Normalbetrieb identisch. Der Unterschied: Mit restart: always startet Docker den Container nach einem System-Reboot auch dann wieder, wenn Sie ihn vorher manuell mit docker compose down gestoppt haben. Mit restart: unless-stopped merkt sich Docker den manuellen Stop — der Container bleibt nach einem Reboot unten. Das ist das gewünschte Verhalten für selbst gehostete Dienste: automatisch hochkommen nach einem Reboot, aber docker compose down bedeutet wirklich, dass der Stack unten bleibt. In der Authelia-SSO-Konfiguration — wo ein versehentlicher Neustart einer Forward-Auth-Komponente den Zugang zu allen dahinter liegenden Services sperren kann — ist das der entscheidende Unterschied.

Das wichtigste Missverständnis zum Thema: restart_policy in standalone Docker Compose reagiert auf den Exit-Code des Hauptprozesses — nicht auf den Healthcheck-Status. Ein Container, der läuft aber unhealthy ist, wird von Compose nicht neu gestartet. Nur Docker Swarm (deploy.restart_policy) berücksichtigt den Gesundheitsstatus als Neustart-Trigger. Für standalone Setups brauchen Sie entweder ein Wrapper-Skript, das den Prozess aktiv beendet, wenn ein Health-Check dauerhaft fehlschlägt, oder ein externes Monitoring-Tool.

Ein durchgehendes Minimalbeispiel: Postgres und eine Web-App

compose.yaml Schritt für Schritt

Das folgende Beispiel zeigt einen Postgres-Container mit sorgfältig konfiguriertem Healthcheck und eine Web-App, die erst startet, wenn die Datenbank tatsächlich bereit ist:

services:
  db:
    image: postgres:17
    restart: unless-stopped
    environment:
      POSTGRES_USER: app
      POSTGRES_PASSWORD: secret
      POSTGRES_DB: appdb
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U app -d appdb"]
      interval: 10s
      timeout: 5s
      retries: 5
      start_period: 30s
      start_interval: 5s
    volumes:
      - db_data:/var/lib/postgresql/data

  web:
    image: ghcr.io/example/myapp:latest
    restart: unless-stopped
    depends_on:
      db:
        condition: service_healthy
    ports:
      - "8080:8080"
    environment:
      DATABASE_URL: postgres://app:secret@db:5432/appdb

volumes:
  db_data:

pg_isready ist im offiziellen Postgres-Image enthalten und der zuverlässigste Test dafür, ob Postgres Verbindungen akzeptiert. Es gibt Exit-Code 0 zurück, wenn der Server erreichbar ist, und Exit-Code 1 bei Problemen. Kein curl, keine HTTP-Anfrage, kein zusätzliches Paket nötig.

depends_on: condition: service_healthy

Wenn docker compose up ausgeführt wird, wechselt web in den Created-Zustand und wartet. Compose prüft regelmäßig den Gesundheitsstatus von db. Erst wenn db healthy meldet, startet web. Ohne condition: service_healthy würde web unmittelbar nach db starten — auch wenn Postgres noch initialisiert.

+--------+  depends_on  +----------+
|  web   | -----------> |    db    |
|(Created)              |(starting)|
+--------+              +----------+
     |                       |
     |               [pg_isready OK]
     |                       |
     |                  +---------+
     +------ startet <--|   db    |
                        |(healthy)|
                        +---------+

Neben service_healthy unterstützt depends_on zwei weitere Bedingungen: service_started (entspricht der Kurzform depends_on: [db]) und service_completed_successfully für Initialisierungs- oder Migrations-Container, die nach Abschluss beendet werden sollen.

Timing und Zustände messen

Wie lange bis unhealthy? Die Formel

Mit den Standardwerten (interval=30s, retries=3, start_period=0s) dauert es maximal 90 Sekunden, bis ein Container unhealthy wird: drei aufeinanderfolgende Fehlschläge à 30 Sekunden. Im Minimalbeispiel oben (start_period=30s, start_interval=5s, interval=10s, retries=5) sieht die Rechnung so aus:

  • Während start_period (30 s): Prüfungen alle 5 s, Fehlschläge werden nicht gewertet
  • Nach start_period: Prüfungen alle 10 s, 5 aufeinanderfolgende Fehlschläge nötig
  • Worst Case bis unhealthy: 30 s + 5 × 10 s = 80 Sekunden

Diese Formel ist relevant für depends_on: condition: service_healthy: Compose wartet, bis der Dienst healthy wird, oder bricht ab, wenn das interne Warte-Timeout überschritten wird. Wer lange Startzeiten hat, sollte start_period entsprechend großzügig ansetzen — ein zu niedrig angesetzter Wert führt zu falschen unhealthy-Meldungen schon beim Erststart.

docker inspect und docker events

Den aktuellen Gesundheitsstatus eines laufenden Containers prüfen:

docker inspect --format='{{json .State.Health}}' <container_name> | jq .

Die Ausgabe enthält Status, FailingStreak und die letzten fünf Prüfergebnisse (Log) mit Exit-Code, Start- und Endzeitpunkt sowie der vollständigen Ausgabe des Prüfbefehls. Das ist die erste Anlaufstelle, wenn ein Service unerwartet unhealthy wird. Docker kürzt die Ausgabe je Log-Eintrag auf 4096 Bytes — Prüfbefehle, die viel auf stdout schreiben, sollten mit > /dev/null oder 2>&1 beruhigt werden.

Echtzeit-Übergänge beobachten:

docker events --filter event=health_status

Dieser Stream zeigt jeden Übergang zwischen starting, healthy und unhealthy mit Zeitstempel — nützlich beim Debuggen von Timing-Problemen in depends_on-Ketten. Eine Übersicht über alle laufenden Container inklusive Healthcheck-Kurzstatus liefert docker ps --format "table {{.Names}}\t{{.Status}}".

Häufige Fragen

Löst ein unhealthy-Status automatisch einen Neustart aus?

Nein. restart_policy in standalone Docker Compose reagiert auf den Exit-Code des Hauptprozesses, nicht auf den Healthcheck-Status. Ein Container, der läuft aber unhealthy ist, wird nicht neu gestartet. Nur Docker Swarm (deploy.restart_policy) berücksichtigt den Gesundheitsstatus. Für standalone Setups muss die Anwendung selbst den Prozess beenden, wenn sie nicht mehr funktionsfähig ist.

Kann ich den Healthcheck eines Basis-Images deaktivieren?

Ja: im Dockerfile mit HEALTHCHECK NONE, in der compose.yaml mit disable: true. Nützlich, wenn ein Debug-Basis-Image einen Healthcheck mitbringt, der im Produktionsbetrieb nicht passt, oder wenn ein externer Mechanismus (Kubernetes Liveness Probe, AWS ECS) das Monitoring übernimmt.

Was tun, wenn der Healthcheck-Befehl im Image nicht verfügbar ist?

Am häufigsten fehlt curl in schlanken Alpine- oder Distroless-Images. Alternativen: wget, pg_isready für PostgreSQL, redis-cli PING für Redis. Wer ein Image nach dem Multi-Stage-Muster aufbaut, kann eine statische Binary in der Build-Stage herunterladen und in die Runtime-Stage kopieren.

Kann depends_on auf mehrere Dienste mit service_healthy warten?

Ja. Die Langform erlaubt beliebig viele Abhängigkeiten:

depends_on:
  db:
    condition: service_healthy
  cache:
    condition: service_healthy
  migrations:
    condition: service_completed_successfully

service_completed_successfully eignet sich für Migrations-Container, die nach Abschluss mit Exit-Code 0 beendet werden sollen. Die Web-App startet erst, wenn sowohl Datenbank als auch Cache healthy melden und die Migrations durchgelaufen sind.

Warum schlägt der Healthcheck sporadisch fehl, obwohl der Service läuft?

Häufigste Ursache: timeout ist zu niedrig. Der Prüfbefehl läuft unter Last länger als erwartet — besonders bei HTTP-Requests gegen eine beschäftigte Anwendung. Ein realistischer Wert liegt zwischen 5 s und 10 s. Die tatsächliche Laufzeit jeder Prüfung ist im Log-Array von docker inspect ablesbar.

Weiterführende Dokumentation

Den vollständig verdrahteten Compose-Stack — eigenes Netzwerk, Volumes, Service-Abhängigkeiten — zeigt der Docker Compose Multi-Service-Post. Wie man Images schlankhält und Werkzeuge wie pg_isready gezielt in Runtime-Images verfügbar macht, zeigt das Multi-Stage-Dockerfile-Beispiel. Für Backups des laufenden Stacks beschreibt der Restic-Beitrag den Weg zu verschlüsselten, automatisierten Sicherungen.

Offizielle Referenz: HEALTHCHECK im Dockerfile · healthcheck in Compose · restart in Compose.

Fragen zu stabilen Container-Stacks beantwortet der IT-Support.

Hinweis: Die Beiträge dieses Blogs werden unter Einsatz von KI erstellt und vor der Veröffentlichung redaktionell geprüft. Die redaktionelle Verantwortung trägt Emre Yurtbay (siehe Impressum).

Projekt besprechen