Zum Hauptinhalt springen
Self-Hosting & Privatsphäre

Docker Compose Healthchecks erklärt

Composes 'Up'-Status bedeutet nur, dass der Prozess gestartet ist. Reale interval/retries/start_period-Werte für drei Homelab-Dienste, plus die Verdrahtung von depends_on: condition: service_healthy.

Milan Buha21. Oktober 20267 Min. LesezeitRead in English
TeilenXin
Docker Compose Healthchecks erklärt

Docker Compose Healthchecks erklärt

docker compose ps zeigte bei jedem Container Up. Das Dashboard hinter einem davon lieferte seit vierzig Minuten 500er-Fehler, bevor es überhaupt jemandem auffiel — ein nachgelagerter Backup-Job versuchte immer wieder, darauf zuzugreifen, und scheiterte immer wieder, ohne dass Composes eigene Statuszeile jemals etwas davon verriet. Up stimmte die ganze Zeit. Es war trotzdem nutzlos.

TL;DR

  • Composes Standardstatus (Up) bedeutet nur, dass der Prozess im Container gestartet ist — er sagt nichts darüber aus, ob die Anwendung darin tatsächlich funktioniert.
  • Ein healthcheck:-Block gibt Compose ein echtes Signal: test, interval, timeout, retries und start_period, abgestimmt pro Dienst.
  • start_period ist das Feld, das am häufigsten falsch gesetzt wird — zu kurz, und ein langsam startender Dienst wird als unhealthy markiert, während er noch hochfährt; zu lang, und eine echte Crash-Loop versteckt sich darin.
  • Kombinieren Sie condition: service_healthy unter dem depends_on eines abhängigen Dienstes, damit nichts gegen einen Container startet, der zwar läuft, aber noch nicht bereit ist — siehe unseren Leitfaden zu depends_on für die Restart-Seite davon.
  • Compose startet einen unhealthy-Container nicht von selbst neu — das ist Aufgabe der restart:-Policy, nicht des Healthchecks.

Warum 'Up' nicht 'Healthy' bedeutet

Standardmäßig verfolgt Docker nur eine Sache: ob der Hauptprozess des Containers noch läuft. Laut der Compose-Datei-Referenz zu Healthchecks hat ein Container ohne healthcheck:-Block keinen weiteren Zustand — er läuft entweder oder er ist gestoppt, nichts dazwischen. Ein Prozess kann am Leben sein und trotzdem völlig kaputt: hängend bei einer Abhängigkeit, verklemmt in einem Deadlock, oder liefert bei jedem Request einen 500er, während seine Event-Loop technisch weiterdreht.

Diese Lücke erzeugt zwei Fehlerbilder:

  • Still tot: Der Prozess lebt, aber die Anwendung darin ist in einen schlechten Zustand abgestürzt und beendet sich nie.
  • Langsamer Start, fälschlich als tot gewertet: Ein Dienst wie ein Medien-Indexer oder eine JVM-Anwendung braucht reale Zeit, um nutzbar zu werden, und eine naive Prüfung markiert ihn als unhealthy, bevor er eine faire Chance hatte.

Ein healthcheck:-Block macht aus "Prozess läuft" ein "Anwendung antwortet tatsächlich" und gibt Compose (und allem, was von diesem Dienst abhängt) ein Signal, auf das reagiert werden kann.

Der healthcheck-Block, Feld für Feld

services:
  db:
    image: postgres:16
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U app"]
      interval: 5s
      timeout: 3s
      retries: 5
      start_period: 10s
  • test: der Befehl, den Compose im Container ausführt. CMD-SHELL führt ihn über die Shell des Containers aus; ein reines CMD-Array überspringt die Shell und vermeidet Überraschungen beim Quoting.
  • interval: wie oft die Prüfung läuft, sobald der Container den start_period hinter sich hat.
  • timeout: wie lange eine einzelne Prüfung laufen darf, bevor sie als Fehlschlag zählt.
  • retries: aufeinanderfolgende Fehlschläge, bevor Compose den Container als unhealthy markiert.
  • start_period: ein Gnadenfenster, in dem Fehlschläge nicht gegen retries zählen — nur ein Erfolg innerhalb dieses Fensters kann es vorzeitig beenden.

Abstimmung für echte Dienste (keine Spielzeug-Defaults)

Jedes Healthcheck-Tutorial zeigt das obige Postgres-Beispiel und hört dort auf. Die Werte, die tatsächlich zählen, sind die, die sich pro Dienst unterscheiden — eine schnell fehlschlagende API und ein langsam startender Medien-Indexer sollten nicht denselben start_period teilen. Das sind die Werte, die ich für drei Dienste auf meinem eigenen Proxmox-Homelab einsetze, ermittelt, indem ich die reale Bootzeit jedes Dienstes in docker compose logs -f beobachtet habe, statt einen Default zu kopieren:

Dienst Boot-Profil interval timeout retries start_period Warum
Postgres (db) Schnell, berechenbar 5s 3s 5 10s WAL-Recovery ist bei einem sauberen Shutdown selten und kurz; ein kurzer start_period erkennt einen wirklich kaputten Container schnell.
Interne REST-API Schnell, hängt aber von db ab 10s 5s 3 15s Etwas längerer start_period, um die eigene DB-Verbindungs-Retry-Schleife der API beim Kaltstart abzufedern.
Jellyfin (Medien-Indexer) Langsam — Bibliotheksscan beim ersten Start 30s 10s 3 120s Ein kalter Bibliotheksscan auf einem großen Medien-Volume kann über zwei Minuten dauern; ein kurzer start_period hier erzeugt nur falsche unhealthy-Flaps.

KEY-STAT: 120s — start_period, den ich für einen langsam startenden Medien-Indexer einsetze, bevor fehlgeschlagene Prüfungen überhaupt zählen: 12x länger als die 10s bei Postgres, eigenes Homelab

Note

Diese Werte stammen daher, dass ich die eigene Bootzeit jedes Containers beobachtet habe, nicht aus einer Formel. Der richtige Weg, start_period zu setzen: einmal docker compose logs -f <dienst> laufen lassen, notieren, wie lange die App unter normalen Bedingungen tatsächlich braucht, um bereit zu sein, dann rund 30–50 % Puffer aufschlagen — nicht eine runde Zahl raten und hoffen.

Die start_period-Ermessensentscheidung

start_period ist das Feld, bei dem die meisten Healthchecks falschliegen, weil es als einziges eine Ermessensentscheidung ist statt etwas Konkretes zu messen. Setzen Sie es zu kurz, wird ein legitim langsam startender Dienst — der Medien-Indexer oben ist der klarste Fall — als unhealthy markiert, während er noch normale Erststart-Arbeit verrichtet, was dann alles blockiert, was mit condition: service_healthy darauf wartet. Setzen Sie es zu lang, sitzt ein Container, der wirklich in einer Crash-Loop feststeckt, minutenlang im Gnadenfenster, bevor Compose überhaupt meldet, dass etwas nicht stimmt.

Warning

Ein start_period, der auf "was auch immer das Geflacker stoppt" statt auf die reale Bootzeit des Dienstes gesetzt wird, ist der häufigste Healthcheck-Fehler überhaupt — er behebt das Bereitschaftsproblem nicht, er versteckt es nur hinter einem längeren Gnadenfenster, bis eines Tages die reale Bootzeit auch über diese Zahl hinauswächst.

Healthchecks mit depends_on: condition: service_healthy verdrahten

Ein Healthcheck allein ändert nur, was docker compose ps meldet. Der eigentliche Nutzen entsteht, wenn Sie ihn mit depends_on verbinden, damit ein abhängiger Dienst nicht startet, bevor die Abhängigkeit tatsächlich bereit ist — nicht nur gestartet:

services:
  db:
    image: postgres:16
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U app"]
      interval: 5s
      timeout: 3s
      retries: 5
      start_period: 10s

  api:
    image: my-api:latest
    depends_on:
      db:
        condition: service_healthy

Ohne condition: service_healthy wartet depends_on laut der Compose-Dokumentation zur Startreihenfolge nur darauf, dass der Container-Prozess von db startet — nicht darauf, dass Postgres die Recovery abschließt und Verbindungen annimmt. Mit dieser Bedingung startet api erst gar nicht, bevor pg_isready Erfolg meldet — genau die Lücke, die dazu führt, dass eine erste Migration oder eine erste Abfrage gegen eine Datenbank fehlschlägt, die Sekunden zuvor technisch "gestartet" war.

Was passiert, wenn ein Container unhealthy wird

Wenn Compose einen Container als unhealthy markiert, startet er ihn nicht automatisch neu — Healthchecks und Restart-Policies sind zwei getrennte Mechanismen. Einen unhealthy-Container diagnostizieren Sie mit:

docker compose ps
docker inspect --format='{{json .State.Health}}' <container> | python3 -m json.tool

Der zweite Befehl gibt die tatsächliche Fehlerausgabe der letzten Prüfversuche aus, was fast immer schneller ist, als allein aus den Logs zu raten. Damit Compose sich tatsächlich vom unhealthy-Zustand erholt, kombinieren Sie den Healthcheck mit einer restart:-Policy — welche Policy zu welchem Fehlerbild passt, behandelt unser Leitfaden zu Docker-Compose-Restart-Policies; ein Healthcheck ohne dahinterliegende Restart-Policy erzeugt nur ein korrektes, aber dauerhaftes "kaputt"-Label.

Häufige Healthcheck-Fehler

  • curl oder wget in einem minimalen Image verwenden, das diese nicht enthält — die Prüfung schlägt sofort mit "command not found" fehl, was in docker compose ps identisch zu einem echten Fehler aussieht. Installieren Sie das Tool bewusst, oder verwenden Sie eine sprachnative Prüfung (Postgres' eigenes pg_isready, ein kleines Skript, oder den bestehenden /health-Endpoint der App über ein Tool, das bereits im Image vorhanden ist).
  • Prüfen, ob ein Port offen ist, statt ob die App bereit ist — ein Port kann TCP-Verbindungen annehmen, bevor die dahinterliegende Anwendung einen echten Request bedienen kann, besonders bei Diensten mit einer Aufwärmphase.
  • retries zu niedrig für einen Dienst unter realer Last — ein Container, der unter Last kurz langsam ist (nicht kaputt), wird als unhealthy markiert und neu gestartet, was schlimmer ist als die vorübergehende Langsamkeit, auf die reagiert wurde. Wer noch entscheidet, welchen Netzwerkmodus ein stark ausgelasteter Dienst nutzen soll, findet in unserem network_mode: host-Erklärstück eine verwandte Quelle für Verbindungs-Flakiness.

FAQ

Wie prüfe ich, ob ein Docker-Container gesund ist?

Führen Sie docker compose ps aus, um die Statusspalte zu sehen, oder docker inspect --format='{{json .State.Health}}' <container> für die vollständige Historie der letzten Prüfversuche und deren Ausgabe.

Was ist start_period bei einem Docker-Healthcheck?

Ein Gnadenfenster nach dem Start des Containers, in dem fehlgeschlagene Prüfungen nicht gegen retries zählen. Nur eine erfolgreiche Prüfung innerhalb dieses Fensters kann es vorzeitig beenden. Es existiert, damit ein langsam startender Dienst nicht als unhealthy markiert wird, bevor er eine faire Chance hatte, den Start abzuschließen.

Warum zeigt mein Container "running", aber die App funktioniert nicht?

Weil Docker ohne einen healthcheck:-Block nur verfolgt, ob der Prozess des Containers lebt — nicht, ob die Anwendung darin tatsächlich korrekt antwortet. Fügen Sie einen Healthcheck hinzu, der echte Bereitschaft prüft, nicht nur, dass der Prozess existiert.

Startet Docker Compose unhealthy-Container automatisch neu?

Nein. Ein Healthcheck ändert nur den gemeldeten Status. Automatische Wiederherstellung erfordert eine separate restart:-Policy auf dem Dienst.

Wie funktioniert depends_on condition service_healthy in Docker Compose?

Ein abhängiger Dienst wartet damit, bis der Healthcheck der Abhängigkeit healthy meldet, bevor er startet — das schließt die Lücke des Standard-depends_on-Verhaltens, das nur darauf wartet, dass der Container-Prozess der Abhängigkeit startet.

Fazit

Up war nie ein Bereitschaftssignal — es ist ein Prozess-lebt-Signal, das nur so aussieht. Ein abgestimmter healthcheck:-Block, zugeschnitten auf die reale Bootzeit jedes Dienstes statt auf einen kopierten Default, macht aus dieser Lücke einen echten Status, auf den Compose (und alles, was vom Dienst abhängt) reagieren kann. Wer den Rest eines Multi-Service-Stacks darum herum aufbaut, findet in unserem Docker-Compose-Self-Hosting-Leitfaden die Grundlagen, die dieser Artikel voraussetzt.

Ähnliche Artikel

Mehr aus Self-Hosting & Privatsphäre

Bleiben Sie auf dem Laufenden

Erhalten Sie die neuesten Artikel direkt in Ihr Postfach. Kein Spam, jederzeit abbestellbar.

Als Nächstes lesen

Tailscale-Alternativen: Headscale & Co.

Tailscales Gratis-Tarif deckelt bei 6 Nutzern. Headscale, Netbird, ZeroTier und Twingate im Vergleich: echte Gratis-Limits, Selbst-Hosting-Aufwand und die tatsächlichen Kosten eines Wechsels.

Weiterlesen