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.

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,retriesundstart_period, abgestimmt pro Dienst. start_periodist 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_healthyunter demdepends_oneines 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-SHELLführt ihn über die Shell des Containers aus; ein reinesCMD-Array überspringt die Shell und vermeidet Überraschungen beim Quoting.interval: wie oft die Prüfung läuft, sobald der Container denstart_periodhinter 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 alsunhealthymarkiert.start_period: ein Gnadenfenster, in dem Fehlschläge nicht gegenretrieszä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
curloderwgetin einem minimalen Image verwenden, das diese nicht enthält — die Prüfung schlägt sofort mit "command not found" fehl, was indocker compose psidentisch zu einem echten Fehler aussieht. Installieren Sie das Tool bewusst, oder verwenden Sie eine sprachnative Prüfung (Postgres' eigenespg_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.
retrieszu 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.
Mehr aus Self-Hosting & Privatsphäre

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.

Jeder aus einer README kopierte Compose-Stack nutzt dieselbe Handvoll Ports. So funktionieren Host- vs. Container-Port und ports vs. expose wirklich, warum Multi-Stack-Homelabs kollidieren, und wann ein Reverse Proxy sinnvoll ist.

Sie zahlen schon für Proton Mail oder Proton VPN. Jetzt schiebt Proton Ihnen immer wieder Pass unter, den eigenen Passwort-Manager, eingebettet ins selbe Abo — und das Angebot klingt verlockend, bis Sie sich ausmalen müssen, wie Sie Jahre gespeicherter Logins umziehen und was dabei kaputtgeht.
Bleiben Sie auf dem Laufenden
Erhalten Sie die neuesten Artikel direkt in Ihr Postfach. Kein Spam, jederzeit abbestellbar.
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