Docker Compose Service-Abhängigkeiten: Healthcheck-Konfiguration löst Startreihenfolge-Probleme mit Datenbanken

Freitagabend, zehn Uhr – in Ihrem Terminal rollen dieselben Fehlermeldungen schon zum zehnten Mal vorbei.
Der Anwendungscontainer startet ständig neu, die Datenbank läuft zwar, ist aber immer einen Schritt zu langsam. Sie prüfen die docker-compose.yml – depends_on ist konfiguriert. Warum funktioniert es trotzdem nicht?
Dieses Problem kennen viele Entwickler. In der lokalen Entwicklungsumgebung schlägt docker-compose up die ersten beiden Starts oft fehl; erst nach zehn bis zwanzig Sekunden und mehreren Neustarts läuft alles stabil.
Die Ursache ist einfach: Docker depends_on steuert nur die Container-Startreihenfolge, nicht ob der Dienst wirklich bereit ist.
In diesem Artikel zeige ich Ihnen Schritt für Schritt:
- die drei condition-Konfigurationen für depends_on (90 % kennen nur den Standardwert)
- die korrekte healthcheck-Konfiguration für PostgreSQL und MySQL (mit vollständigen Beispielen)
- die moderne Alternative zu wait-for-it-Skripten
- eine Checkliste zur Fehlerbehebung für zuverlässige Container-Starts
Warum depends_on nicht ausreicht: Start ≠ Bereitschaft
In der Docker-Dokumentation steht ein oft übersehener Satz:
Compose does not wait until a container is “ready”, only until it’s running.
Bedeutung: Compose wartet nur, bis der Container läuft – nicht, bis der Dienst tatsächlich nutzbar ist.
Zeitlücke zwischen Container-Start und Service-Bereitschaft
Stellen Sie sich den Startvorgang eines PostgreSQL-Containers vor:
- 0 s: Docker startet den Container, postgres-Prozess startet ← depends_on gibt hier schon frei
- 2 s: Initialisierung des Datenverzeichnisses
- 5 s: Laden der Konfigurationsdateien
- 8 s: Ausführung von Init-Skripten (falls vorhanden)
- 12 s: endlich bereit, Verbindungen werden akzeptiert
Dazwischen liegt eine Lücke von rund 12 Sekunden. Versucht Ihre Web-App in Sekunde 1 eine Verbindung zur Datenbank, ist das Ergebnis zwangsläufig „Connection refused”.
In der Praxis kann es noch extremer sein. Bei einem Legacy-Projekt, das ich betreute, importierte ein Initialisierungsskript 500 MB Testdaten – allein das dauerte 40 Sekunden. Mit der Standard-depends_on-Konfiguration musste der Anwendungscontainer mindestens fünfmal abstürzen und neu starten, bevor die Verbindung klappte.
Die drei condition-Werte von depends_on
Viele wissen nicht, dass depends_on drei condition-Werte unterstützt:
services:
web:
depends_on:
db:
condition: service_started # Standard: Container läuft, fertig
# condition: service_healthy # Warten auf bestandenen Healthcheck
# condition: service_completed_successfully # Warten auf erfolgreichen Exit (für Init-Container)
service_started (Standard): Sobald der Container im Status running ist, geht es weiter. Deshalb reicht depends_on allein oft nicht aus.
service_healthy: Der Healthcheck muss bestanden sein, der Container-Status muss „healthy” sein. Genau das brauchen wir in den meisten Fällen.
service_completed_successfully: Wartet auf erfolgreichen Exit (Exit-Code 0). Geeignet für einmalige Aufgaben wie Datenmigrationen.
Warum ist service_healthy nicht der Standard?
Sie fragen sich vielleicht: Wenn service_healthy so nützlich ist – warum ist es nicht der Standard?
Zwei Gründe:
- Nicht jeder Dienst braucht einen Healthcheck (z. B. reine stateless Worker)
- Healthchecks müssen Sie selbst konfigurieren – Docker weiß nicht, wann Ihr Dienst „bereit” ist
Das führt zum nächsten Thema: Wie konfiguriert man Healthchecks?
Healthcheck – vollständige Konfigurationsanleitung
Das Prinzip ist einfach: Docker führt regelmäßig einen Befehl aus. Exit-Code 0 bedeutet gesund, Exit-Code 1 bedeutet ungesund.
Vollständige healthcheck-Konfiguration
services:
db:
image: postgres:16
healthcheck:
test: ["CMD-SHELL", "pg_isready -U postgres"] # Prüfbefehl
interval: 10s # alle 10 Sekunden prüfen
timeout: 5s # Timeout pro Prüfung: 5 Sekunden
retries: 3 # erst nach 3 Fehlversuchen als unhealthy markieren
start_period: 30s # Fehler in den ersten 30 Sekunden zählen nicht zu retries
Alle fünf Parameter sind wichtig – gehen wir sie der Reihe nach durch.
test: Prüfbefehl
Es gibt zwei Formate:
# Variante 1: Shell verwenden (empfohlen)
test: ["CMD-SHELL", "pg_isready -U postgres"]
# Variante 2: Befehl direkt ausführen (ohne Shell)
test: ["CMD", "pg_isready", "-U", "postgres"]
In den meisten Fällen reicht CMD-SHELL, weil Sie Shell-Features wie Pipes und Umleitungen nutzen können.
Häufige Falle: Das Prüfwerkzeug muss im Image vorhanden sein. Prüfen Sie einen HTTP-Endpunkt mit curl, das Image enthält aber kein curl – der Healthcheck schlägt dauerhaft fehl. Mir ist das schon passiert; die Lösung war eine Zeile RUN apk add curl im Dockerfile.
interval: Prüfintervall
Wie oft geprüft wird. Zu häufig kostet Ressourcen, zu selten reagiert das System träge.
- 10 s ist ein guter Standard für die meisten Szenarien
- Bei kritischen Datenbanken können 5 s sinnvoll sein
- Bei leichten Diensten auch 15–30 s
timeout: Timeout pro Prüfung
Maximale Wartezeit für eine einzelne Prüfung. Hängt der Befehl, wartet Docker bis zum Timeout und bricht dann ab.
Zu kurz führt zu Fehlalarmen, zu lang verlangsamt die Fehlererkennung. 5–10 s sind ein sicherer Bereich.
retries: Anzahl der Fehlversuche
Wie oft hintereinander fehlgeschlagen werden muss, bevor der Status unhealthy wird.
Das ist ein Entprellmechanismus. Netzwerk-Jitter oder kurzzeitige Lastspitzen können einzelne Prüfungen scheitern lassen – retries machen das System robuster.
3–5 ist sinnvoll. retries=1 ist zu empfindlich, retries=10 zu träge.
start_period: Start-Schonfrist (am leichtesten zu übersehen)
Fehler innerhalb von start_period zählen nicht zu retries. Der Dienst bekommt so eine „Start-Pufferzeit”.
Warum wichtig? Datenbanken brauchen Startzeit. PostgreSQL initialisiert Datenverzeichnisse, MySQL lädt Tabellenindizes. Ohne start_period zählt der Healthcheck schon in Sekunde 2 Fehler – möglicherweise ist der Dienst unhealthy, bevor retries überhaupt ausgeschöpft sind.
Empfohlene Werte:
- PostgreSQL/MySQL: 30–60 s
- Leichte Dienste (Redis): 15–30 s
- Viele Init-Skripte: bis 120 s
Ich setze meist 60 s – lieber etwas länger warten als Fehlalarme riskieren.
Häufige Fehler und wie Sie sie vermeiden
Fehler 1: Umgebungsvariablen falsch referenziert
# ❌ Falsch: Compose interpoliert vor dem Start – der Container bekommt den Wert vom Host
test: ["CMD", "mysqladmin", "ping", "-p$MYSQL_ROOT_PASSWORD"]
# ✅ Richtig: $$ escapen, damit die Shell im Container auflöst
test: ["CMD-SHELL", "mysqladmin ping -p$$MYSQL_ROOT_PASSWORD"]
Fehler 2: start_period zu kurz
# ❌ Datenbank noch nicht initialisiert, Fehlerzähler läuft schon
healthcheck:
test: ["CMD", "pg_isready"]
interval: 5s
retries: 3
start_period: 10s # zu kurz!
# ✅ Genug Startzeit einplanen
healthcheck:
start_period: 60s # deutlich entspannter
Fehler 3: Prüfwerkzeug fehlt im Image
Dieser Fehler ist besonders tückisch, weil Docker still scheitert.
# ❌ Wenn curl im Image fehlt, schlägt der Healthcheck dauerhaft fehl
test: ["CMD", "curl", "-f", "http://localhost/health"]
# ✅ Werkzeug sicherstellen oder ein im Image vorhandenes nutzen
test: ["CMD", "wget", "--spider", "http://localhost/health"] # Alpine-Images haben wget
Healthcheck konfiguriert – als Nächstes konkrete Datenbank-Setups.
PostgreSQL Healthcheck – Praxis-Konfiguration
Das offizielle PostgreSQL-Image bringt ein sehr nützliches Werkzeug mit: pg_isready.
Es prüft gezielt, ob PostgreSQL bereit ist – zuverlässiger als eine selbstgeschriebene SQL-Abfrage.
Basiskonfiguration (empfohlen)
version: '3.8'
services:
web:
image: node:20-alpine
depends_on:
db:
condition: service_healthy # entscheidend: auf Healthcheck warten
restart: true # bei DB-Neustart startet die App mit neu
environment:
DATABASE_URL: postgresql://postgres:password@db:5432/myapp
command: npm start
db:
image: postgres:16
environment:
POSTGRES_USER: postgres
POSTGRES_PASSWORD: password
POSTGRES_DB: myapp
healthcheck:
test: ["CMD-SHELL", "pg_isready -U postgres -d myapp"]
interval: 10s
timeout: 5s
retries: 3
start_period: 60s
volumes:
- postgres_data:/var/lib/postgresql/data
volumes:
postgres_data:
pg_isready im Detail
pg_isready -U postgres -d myapp
-U: Benutzername – muss existieren-d: Datenbankname (optional, aber empfohlen)
Warum -U? Ohne Angabe versucht pg_isready den aktuellen Systembenutzer – das erzeugt Warnungen in den Logs. Funktional meist unkritisch, aber unschön.
Erweiterte Konfiguration: echte Abfrage
pg_isready prüft nur Erreichbarkeit, nicht ob Abfragen wirklich funktionieren. Für strengere Prüfungen:
healthcheck:
test: ["CMD-SHELL", "pg_isready -U postgres && psql -U postgres -d myapp -c 'SELECT 1'"]
interval: 10s
timeout: 10s # Timeout erhöhen wegen der zusätzlichen Abfrage
retries: 3
start_period: 60s
SELECT 1 ist die einfachste Abfrage – wenn sie klappt, kann die Datenbank SQL verarbeiten.
Für die meisten Szenarien reicht das Basis-pg_isready.
Laufzeitverhalten
Nach der Konfiguration starten Sie so:
$ docker-compose up
Creating network "myapp_default" ... done
Creating myapp_db_1 ... done
Waiting for myapp_db_1 to be healthy... ← diese Zeile beachten
Creating myapp_web_1 ... done
db_1 | PostgreSQL init process complete; ready for start up.
db_1 | database system is ready to accept connections
web_1 | Server listening on port 3000 ← App startet erst nach DB-Bereitschaft
Sie sehen eine spürbare Pause – Docker wartet, bis db healthy ist. Das kann 30–60 Sekunden dauern, dafür startet alles ohne Fehler.
Fehlerbehebung: Container bleibt unhealthy
Bleibt der Datenbank-Container dauerhaft unhealthy, prüfen Sie die Healthcheck-Logs:
# Health-Status des Containers anzeigen
$ docker inspect --format='{{json .State.Health}}' myapp_db_1 | jq
{
"Status": "unhealthy",
"FailingStreak": 5,
"Log": [
{
"Start": "2024-12-17T03:15:30Z",
"End": "2024-12-17T03:15:30Z",
"ExitCode": 1,
"Output": "pg_isready: could not connect to server: Connection refused"
}
]
}
Häufige Ursachen:
- start_period zu kurz: Datenbank initialisiert noch, Fehlerzähler läuft schon
- Falscher Benutzer- oder Datenbankname: pg_isready kann nicht verbinden
- PostgreSQL-Start fehlgeschlagen: Container-Logs prüfen mit
docker logs myapp_db_1
MySQL Healthcheck – Praxis-Konfiguration
Für MySQL nutzen Sie mysqladmin ping – ein in MySQL enthaltenes Verwaltungstool.
Basiskonfiguration (empfohlen)
version: '3.8'
services:
web:
image: node:20-alpine
depends_on:
db:
condition: service_healthy
restart: true
environment:
DATABASE_URL: mysql://root:password@db:3306/myapp
command: npm start
db:
image: mysql:8.0
environment:
MYSQL_ROOT_PASSWORD: password
MYSQL_DATABASE: myapp
healthcheck:
test: ["CMD", "mysqladmin", "ping", "-h", "localhost", "-u", "root", "-ppassword"]
interval: 10s
timeout: 5s
retries: 3
start_period: 60s
volumes:
- mysql_data:/var/lib/mysql
volumes:
mysql_data:
mysqladmin ping im Detail
mysqladmin ping -h localhost -u root -ppassword
-h: Host (im Container: localhost)-u: Benutzername-p: Passwort (ohne Leerzeichen nach-p)
Bei laufendem MySQL liefert der Befehl:
mysqld is alive
Exit-Code 0 – Healthcheck bestanden.
Passwort korrekt handhaben
Variante 1: Passwort direkt (für Entwicklung)
healthcheck:
test: ["CMD", "mysqladmin", "ping", "-h", "localhost", "-u", "root", "-ppassword"]
Einfach, aber das Passwort steht fest in der Konfiguration.
Variante 2: Umgebungsvariable (empfohlen)
db:
environment:
MYSQL_ROOT_PASSWORD: password
healthcheck:
test: ["CMD-SHELL", "mysqladmin ping -h localhost -u root -p$$MYSQL_ROOT_PASSWORD"]
# Hinweis: $$ statt $
Wichtig: $$ statt $.
Warum? Docker Compose interpoliert Umgebungsvariablen vor dem Start. Mit $MYSQL_ROOT_PASSWORD sucht Compose auf dem Host – mit $$ überlässt Compose die Auflösung der Shell im Container.
Variante 3: ohne Passwort (einfach, umstritten)
healthcheck:
test: ["CMD", "mysqladmin", "ping", "-h", "localhost"]
Manche MySQL-Konfigurationen erlauben lokale Verbindungen ohne Passwort – am einfachsten, für Produktion nicht empfohlen.
Besonderheiten bei MySQL 8.0
MySQL 8.0 nutzt standardmäßig das Authentifizierungs-Plugin caching_sha2_password. Ältere Clients können Probleme haben. Bei Authentifizierungsfehlern können Sie das alte Plugin erzwingen:
db:
image: mysql:8.0
command: --default-authentication-plugin=mysql_native_password
environment:
MYSQL_ROOT_PASSWORD: password
MYSQL_DATABASE: myapp
healthcheck:
test: ["CMD", "mysqladmin", "ping", "-h", "localhost", "-u", "root", "-ppassword"]
interval: 10s
timeout: 5s
retries: 3
start_period: 60s
Häufige Probleme
Problem 1: Access denied for user ‘root’@‘localhost’
Falsches Passwort oder Umgebungsvariable nicht aktiv. Prüfen Sie:
- Schreibweise von MYSQL_ROOT_PASSWORD
- Passwort im healthcheck
- Ob
$$korrekt escapet ist
Problem 2: Container startet langsam, bleibt lange im Status starting
MySQL braucht Zeit für die Erstinitialisierung. start_period mindestens 60 s – bei großen Init-Skripten eher 120 s oder mehr.
Problem 3: Healthcheck bestanden, App verbindet trotzdem nicht
Oft Netzwerk- oder App-Konfiguration. Prüfen Sie:
- Connection-String der App (Hostname
db, nichtlocalhost) - Docker-Netzwerk
docker network inspect– alle Container im gleichen Netzwerk?
Healthchecks für andere Datenbanken und Dienste
Mit PostgreSQL und MySQL als Vorlage sind andere Dienste schnell abgedeckt. Kurzreferenz:
Redis
redis:
image: redis:7-alpine
healthcheck:
test: ["CMD", "redis-cli", "ping"]
interval: 10s
timeout: 3s
retries: 3
start_period: 15s
redis-cli ping liefert PONG, Exit-Code 0. Redis startet schnell – start_period kann kürzer sein.
MongoDB
mongo:
image: mongo:7
healthcheck:
test: ["CMD", "mongosh", "--eval", "db.adminCommand('ping')"]
interval: 10s
timeout: 5s
retries: 3
start_period: 30s
Hinweis: Ab MongoDB 6.0 heißt das Tool mongosh statt mongo. Für ältere Versionen:
test: ["CMD", "mongo", "--eval", "db.adminCommand('ping')"]
RabbitMQ
rabbitmq:
image: rabbitmq:3-management-alpine
healthcheck:
test: ["CMD", "rabbitmq-diagnostics", "ping"]
interval: 10s
timeout: 5s
retries: 3
start_period: 40s
RabbitMQ startet relativ langsam – start_period mindestens 40 s.
Generische HTTP-Dienste
Bietet der Dienst einen Health-Endpunkt (/health oder /ping), nutzen Sie wget oder curl:
api:
image: myapp:latest
healthcheck:
test: ["CMD", "wget", "--spider", "--quiet", "http://localhost:8080/health"]
# oder mit curl (falls im Image vorhanden)
# test: ["CMD", "curl", "-f", "http://localhost:8080/health"]
interval: 10s
timeout: 3s
retries: 3
start_period: 20s
--spider prüft nur ohne Download, --quiet unterdrückt Log-Ausgabe.
Hinweis: wget oder curl muss im Image sein. Alpine hat standardmäßig wget, Debian/Ubuntu oft curl.
Dienste ohne spezielles Prüfwerkzeug
Ohne dediziertes Tool können Sie mit netcat (nc) den Port prüfen:
service:
image: some-service:latest
healthcheck:
test: ["CMD-SHELL", "nc -z localhost 9000 || exit 1"]
interval: 10s
timeout: 3s
retries: 3
start_period: 30s
nc -z prüft nur, ob der Port offen ist – grober als echte Bereitschaftsprüfungen.
wait-for-it-Skripte: braucht man die noch?
Bei Recherchen zu Docker-Startreihenfolgen stoßen Sie oft auf wait-for-it oder wait-for-Skripte.
Die Idee: Im entrypoint der App wartet ein Skript, bis der TCP-Port des abhängigen Dienstes erreichbar ist.
Klassisches wait-for-it-Setup
web:
image: node:20-alpine
depends_on:
- db # nur einfaches depends_on, kein Health-Status
volumes:
- ./wait-for-it.sh:/wait-for-it.sh # Skript mounten
command: ["/wait-for-it.sh", "db:5432", "--", "npm", "start"]
wait-for-it.sh prüft db:5432 in einer Schleife und startet erst dann npm start.
Warum wird das nicht mehr empfohlen?
Best Practice 2024: Native healthcheck statt Skript, wenn möglich.
Gründe:
- Klarere Konfiguration: Health-Logik beim Datenbank-Service, Abhängigkeiten auf einen Blick
- Keine Extra-Dateien: kein Skript, kein Volume-Mount
- Stärkere Prüfung: healthcheck prüft echte Bereitschaft, TCP-Port-Check ist grob
- Bessere Wiederverwendbarkeit: healthcheck einmal konfiguriert, alle abhängigen Services profitieren
Ein oft zitiertes Community-Artikel-Titel lautet sinngemäß: „Forget wait-for-it, use docker-compose healthcheck and depends_on instead”.
Wann wait-for-it trotzdem sinnvoll ist
Fall 1: Image oder Compose nicht änderbar
Third-Party-Image ohne healthcheck und ohne Schreibrechte – dann ist wait-for-it auf App-Seite oft die einzige Option.
Fall 2: Warten auf mehrere Dienste
./wait-for-it.sh db:5432 redis:6379 rabbitmq:5672 -- npm start
depends_on unterstützt mehrere Abhängigkeiten, aber wait-for-it ist in einem Befehl kompakter. Selten nötig.
Weitere Alternativen
Neben wait-for-it gibt es:
- dockerize: Go-basiert, mehr Features, Umgebungsvariablen-Templates
- wait-for: vereinfachte Shell-Variante von wait-for-it
- docker-compose-wait: Python, unterstützt HTTP-Checks
Ehrlich gesagt: Wenn healthcheck möglich ist, lohnt sich der Aufwand für diese Tools selten.
Checkliste zur Fehlerbehebung und Best Practices
Konfiguration steht, es klappt trotzdem nicht? Mit dieser Checkliste lösen Sie rund 90 % der Fälle.
Schnelle Diagnose-Befehle
# 1. Status aller Container
$ docker-compose ps
NAME COMMAND SERVICE STATUS PORTS
myapp_db postgres db healthy 5432/tcp
myapp_web npm start web running 0.0.0.0:3000->3000/tcp
# 2. Healthcheck-Details
$ docker inspect --format='{{json .State.Health}}' myapp_db_1 | jq
# 3. Container-Logs
$ docker-compose logs db
$ docker-compose logs web
# 4. Logs live verfolgen
$ docker-compose logs -f --tail=100
Entscheidungsbaum für häufige Probleme
Problem: Container bleibt starting, wird nie healthy
- start_period zu kurz? → auf 60 s erhöhen
- Healthcheck-Befehl korrekt? → mit
docker inspectprüfen - Befehl manuell im Container testen →
docker exec -it myapp_db_1 pg_isready -U postgres
Problem: unhealthy, dann wieder healthy – hin und her
- interval zu kurz bei knappen Ressourcen → 10 s oder 15 s
- retries zu niedrig → auf 5 erhöhen
- echte DB-Performance-Probleme → DB-Logs prüfen
Problem: Healthcheck OK, App verbindet trotzdem nicht
- Connection-String: Hostname = Service-Name (
db), nichtlocalhost - Port: Container-zu-Container = interner Port (5432), nicht gemappten Host-Port
- Netzwerk: alle Services im gleichen network
Best Practices für Produktion
1. Empfohlene Parameter (konservativ)
healthcheck:
interval: 10s # Balance zwischen Reaktionszeit und Ressourcen
timeout: 5s # genug Zeit für den Befehl
retries: 5 # toleriert sporadische Fehler
start_period: 60s # ausreichend DB-Startzeit
Stabil in den meisten Szenarien. Bei großen Init-Skripten start_period bis 120 s.
2. restart-Strategie
web:
depends_on:
db:
condition: service_healthy
restart: true # bei DB-Neustart startet die App mit neu
restart: unless-stopped # Container nach Exit automatisch neu starten
restart: true sorgt dafür, dass abhängige Services nach DB-Upgrade oder -Neustart neu verbinden.
3. Ressourcenlimits
Healthchecks verbrauchen wenig, aber etwas. Bei knappen Ressourcen:
healthcheck:
interval: 30s # seltener prüfen
timeout: 3s # kürzerer Timeout
In der Praxis ist der Overhead meist vernachlässigbar – außer bei Hunderten von Containern.
4. Health-Status überwachen
In Produktion lohnt Monitoring. Docker-Health-Events lassen sich von Prometheus, Grafana usw. erfassen.
# docker-compose.yml
services:
db:
healthcheck:
test: ["CMD-SHELL", "pg_isready -U postgres"]
interval: 10s
timeout: 5s
retries: 3
start_period: 60s
labels:
- "prometheus.io/scrape=true" # Prometheus kann Health-Status scrapen
5. Mehrere Umgebungen
Entwicklung und Produktion können unterschiedlich konfiguriert sein:
# docker-compose.yml (Entwicklung)
db:
healthcheck:
start_period: 30s # wenig Daten, schneller Start
# docker-compose.prod.yml (Produktion)
db:
healthcheck:
start_period: 120s # viele Daten, langsamer Start
interval: 5s # häufigere Prüfung
Start mit mehreren Dateien:
docker-compose -f docker-compose.yml -f docker-compose.prod.yml up
Debug-Tipps
Tipp 1: Healthcheck-Befehl manuell testen
Container betreten und Befehl ausführen:
$ docker exec -it myapp_db_1 sh
/# pg_isready -U postgres -d myapp
/var/run/postgresql:5432 - accepting connections
/# echo $?
0 ← Exit-Code 0 = Erfolg
Tipp 2: Healthcheck vorübergehend deaktivieren
Beim Debuggen healthcheck auskommentieren, einfaches depends_on nutzen:
web:
depends_on:
- db # vorübergehend einfacher Modus
# db:
# condition: service_healthy
Verbindung klappt? Dann healthcheck wieder aktivieren und dort weiter suchen.
Tipp 3: Docker-Ereignisprotokoll
Docker protokolliert alle Container-Ereignisse inklusive Health-Status:
$ docker events --filter 'event=health_status'
2024-12-17T03:15:30.123456789Z container health_status: healthy (name=myapp_db_1)
2024-12-17T03:16:45.987654321Z container health_status: unhealthy (name=myapp_db_1)
So sehen Sie, wann unhealthy wurde – kombiniert mit Log-Zeitstempeln zur Ursachenanalyse.
Fazit
Zurück zur Eingangsfrage: Warum reicht depends_on nicht?
Kurz gesagt: Es reicht nicht aus.
depends_on wartet standardmäßig nur auf Container-Start, nicht auf Service-Bereitschaft. Ein laufender DB-Container heißt noch nicht „Verbindungen möglich” – genau in dieser Lücke landen die meisten Fehler.
Die Lösung ist straightforward:
- Datenbank mit healthcheck – pg_isready oder mysqladmin ping prüft echten Bereitschaftsstatus
- condition: service_healthy – App wartet auf bestandenen Healthcheck
- Angemessener start_period – mindestens 60 s für DB-Initialisierung
Damit verschwinden die meisten Startfehler.
Schnelle nächste Schritte:
- Sofort: PostgreSQL- oder MySQL-Block aus dem Artikel kopieren, Umgebungsvariablen anpassen
- Heute: depends_on im Projekt auf service_healthy umstellen
- Team: Konfiguration teilen und Standards vereinheitlichen
Falls Ihr Fall hier nicht vorkommt – gerne in den Kommentaren melden. Bei Docker Compose gibt es noch viele Stolpersteine; gemeinsam lassen sie sich abarbeiten.
Wenn dieser Artikel Ihr langjähriges Startproblem gelöst hat, freue ich mich über Feedback. Genau solche „Endlich läuft es”-Momente sind der Grund, warum ich das aufgeschrieben habe.
Vollständiger Workflow für Docker Compose Healthcheck-Konfiguration
Lösung für Anwendungsstartfehler, wenn die Datenbank noch nicht bereit ist – inklusive vollständiger PostgreSQL- und MySQL-Konfigurationsvorlagen
⏱️ Estimated time: 30 min
- 1
Step 1: Ursache verstehen: Grenzen von depends_on
Ursache:
• Docker depends_on steuert nur die Container-Startreihenfolge, nicht ob der Dienst wirklich bereit ist
• Compose wartet nur, bis der Container läuft – nicht, bis der Dienst tatsächlich nutzbar ist
• Docker-Dokumentation: Compose does not wait until a container is „ready“, only until it's running
• Bedeutung: Compose wartet nur, bis der Container läuft – nicht, bis der Dienst tatsächlich nutzbar ist
Zeitlücke zwischen Container-Start und Service-Bereitschaft:
• 0 s: Docker startet den Container, postgres-Prozess startet (depends_on gibt hier schon frei)
• 2 s: Initialisierung des Datenverzeichnisses
• 5 s: Laden der Konfigurationsdateien
• 8 s: Ausführung von Init-Skripten (falls vorhanden)
• 12 s: endlich bereit, Verbindungen werden akzeptiert
Dazwischen liegt eine Lücke von rund 12 Sekunden. Versucht Ihre Web-App in Sekunde 1 eine Verbindung zur Datenbank, ist das Ergebnis zwangsläufig Connection refused. - 2
Step 2: Drei condition-Konfigurationen für depends_on
Drei condition-Konfigurationen für depends_on:
1. service_started (Standard)
• Wartet nur auf Container-Start, nicht auf Service-Bereitschaft
• Nicht empfohlen
2. service_healthy (empfohlen)
• Wartet auf Container-Start und bestandenen Healthcheck
• Stellt sicher, dass der Dienst wirklich bereit ist
3. service_completed_successfully
• Wartet, bis der Container erfolgreich beendet wurde
• Geeignet für Einmal-Aufgaben
Empfehlung service_healthy:
• In docker-compose.yml konfigurieren: depends_on: db: condition: service_healthy
• Stellt sicher, dass die Anwendung erst startet, wenn die Datenbank wirklich bereit ist - 3
Step 3: Korrekte healthcheck-Konfiguration für PostgreSQL und MySQL
PostgreSQL Healthcheck:
• pg_isready prüft, ob die Datenbank bereit ist
• Konfigurationsbeispiel:
healthcheck:
test: ["CMD-SHELL", "pg_isready -U postgres"]
interval: 5s
timeout: 5s
retries: 5
start_period: 60s (genug Zeit für die Initialisierung)
MySQL Healthcheck:
• mysqladmin ping prüft, ob die Datenbank bereit ist
• Konfigurationsbeispiel:
healthcheck:
test: ["CMD-SHELL", "mysqladmin ping -h localhost -u root -p$MYSQL_ROOT_PASSWORD"]
interval: 5s
timeout: 5s
retries: 5
start_period: 60s
Vollständiges Beispiel:
• healthcheck in docker-compose.yml konfigurieren (Prüfbefehl, Intervall, Timeout, Wiederholungen)
• condition: service_healthy in depends_on verwenden
• Stellt sicher, dass die Anwendung erst startet, wenn die Datenbank wirklich bereit ist - 4
Step 4: Fehlerbehebung und Best Practices
Checkliste zur Fehlerbehebung:
1. Prüfen, ob der Healthcheck-Befehl korrekt ist
• Healthcheck-Befehl manuell im Container ausführen und Exit-Code 0 bestätigen
• Beispiel: docker exec -it db_container pg_isready -U postgres
2. Healthcheck vorübergehend deaktivieren
• Beim Debuggen healthcheck auskommentieren
• Einfaches depends_on verwenden, um Healthcheck-Probleme auszuschließen
3. Docker-Ereignisprotokoll prüfen
• docker events zeigt Container-Start- und Healthcheck-Ereignisse
Best Practices:
1. Datenbank mit healthcheck ausstatten – pg_isready oder mysqladmin ping prüft den tatsächlichen Bereitschaftsstatus
2. condition: service_healthy verwenden, damit die Anwendung auf bestandenen Healthcheck wartet
3. Angemessenen start_period setzen (mindestens 60 s), damit die Datenbank genug Initialisierungszeit hat
Mit diesen drei Maßnahmen verschwinden die meisten Container-Startprobleme.
Moderne Alternative zu wait-for-it-Skripten:
• depends_on + healthcheck kombinieren
• Keine zusätzlichen Skripte nötig, einfache Konfiguration, hohe Zuverlässigkeit
• Geeignet für alle Docker Compose-Projekte
FAQ
Warum reicht depends_on nicht aus? Was ist der Unterschied zwischen Container-Start und Service-Bereitschaft?
In der Docker-Dokumentation steht ein oft übersehener Satz: Compose does not wait until a container is „ready“, only until it's running. Bedeutung: Compose wartet nur, bis der Container läuft – nicht, bis der Dienst tatsächlich nutzbar ist.
Zeitlücke zwischen Container-Start und Service-Bereitschaft – der Startvorgang eines PostgreSQL-Containers:
• 0 s: Docker startet den Container, postgres-Prozess startet (depends_on gibt hier schon frei)
• 2 s: Initialisierung des Datenverzeichnisses
• 5 s: Laden der Konfigurationsdateien
• 8 s: Ausführung von Init-Skripten (falls vorhanden)
• 12 s: endlich bereit, Verbindungen werden akzeptiert
Dazwischen liegt eine Lücke von rund 12 Sekunden. Versucht Ihre Web-App in Sekunde 1 eine Verbindung zur Datenbank, ist das Ergebnis zwangsläufig Connection refused.
Welche drei condition-Konfigurationen gibt es für depends_on?
1) service_started (Standard):
• Wartet nur auf Container-Start, nicht auf Service-Bereitschaft
• Nicht empfohlen
2) service_healthy (empfohlen):
• Wartet auf Container-Start und bestandenen Healthcheck
• Stellt sicher, dass der Dienst wirklich bereit ist
• In docker-compose.yml: depends_on: db: condition: service_healthy
3) service_completed_successfully:
• Wartet, bis der Container erfolgreich beendet wurde
• Geeignet für Einmal-Aufgaben
Empfehlung service_healthy: Stellt sicher, dass die Anwendung erst startet, wenn die Datenbank wirklich bereit ist.
Wie konfiguriert man Healthchecks für PostgreSQL und MySQL?
• pg_isready prüft, ob die Datenbank bereit ist
• Konfigurationsbeispiel:
healthcheck:
test: ["CMD-SHELL", "pg_isready -U postgres"]
interval: 5s
timeout: 5s
retries: 5
start_period: 60s (genug Zeit für die Initialisierung)
MySQL Healthcheck:
• mysqladmin ping prüft, ob die Datenbank bereit ist
• Konfigurationsbeispiel:
healthcheck:
test: ["CMD-SHELL", "mysqladmin ping -h localhost -u root -p$MYSQL_ROOT_PASSWORD"]
interval: 5s
timeout: 5s
retries: 5
start_period: 60s
Vollständiges Beispiel: healthcheck in docker-compose.yml konfigurieren (Prüfbefehl, Intervall, Timeout, Wiederholungen), condition: service_healthy in depends_on verwenden – so startet die Anwendung erst, wenn die Datenbank wirklich bereit ist.
Wie behebt man Probleme mit Healthchecks?
1) Prüfen, ob der Healthcheck-Befehl korrekt ist:
• Healthcheck-Befehl manuell im Container ausführen und Exit-Code 0 bestätigen
• Beispiel: docker exec -it db_container pg_isready -U postgres
• Exit-Code 0 bedeutet Erfolg
2) Healthcheck vorübergehend deaktivieren:
• Beim Debuggen healthcheck auskommentieren
• Einfaches depends_on verwenden, um Healthcheck-Probleme auszuschließen
• Nach erfolgreicher Verbindung healthcheck wieder aktivieren
3) Docker-Ereignisprotokoll prüfen:
• docker events zeigt Container-Start- und Healthcheck-Ereignisse
Was sind Best Practices für Healthcheck-Konfiguration?
1) Datenbank mit healthcheck ausstatten – pg_isready oder mysqladmin ping prüft den tatsächlichen Bereitschaftsstatus
2) condition: service_healthy verwenden, damit die Anwendung auf bestandenen Healthcheck wartet
3) Angemessenen start_period setzen (mindestens 60 s), damit die Datenbank genug Initialisierungszeit hat
Mit diesen drei Maßnahmen verschwinden die meisten Container-Startprobleme.
Moderne Alternative zu wait-for-it-Skripten:
• depends_on + healthcheck kombinieren
• Keine zusätzlichen Skripte nötig, einfache Konfiguration, hohe Zuverlässigkeit
• Geeignet für alle Docker Compose-Projekte
Schnelle Handlungsempfehlungen:
• Sofort umsetzbar: PostgreSQL- oder MySQL-Konfiguration aus dem Artikel kopieren und Umgebungsvariablen anpassen
• Heute Abend: depends_on im Team-Projekt auf service_healthy upgraden
• Nächstes Team-Meeting: Mit Kollegen teilen und Konfigurationsstandards vereinheitlichen
13 Min. Lesezeit · Veröffentlicht am: 17. Dez. 2025 · 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 Multi-Service-Orchestrierung: Lokale Entwicklungsumgebung mit einem Befehl starten
Mit Docker Compose mehrere Services orchestrieren und Web, API, MySQL und Redis lokal per Ein-Klick starten. Schluss mit manueller Installation, Versionskonflikten und Portbelegung – neue Teammitglieder klonen das Repo und sind in 5 Minuten startklar, Projektwechsel in Sekunden.
Teil 8 von 38
Nächster
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
Ä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