echte Startreihenfolge statt Port-Raten
Ein offener Datenbank-Port bedeutet noch lange nicht, dass MySQL bereit ist, Verbindungen zu verarbeiten. depends_on mit condition service_healthy verknuepft die Startreihenfolge von Docker Compose Services direkt mit dem Healthcheck-Status, statt sich auf ein reines Erreichbarkeits-Signal zu verlassen, und beseitigt damit eine der haeufigsten Fehlerquellen in Multi-Service-Stacks.
Inhaltsverzeichnis
- 1. Warum ein offener Port keine Betriebsbereitschaft bedeutet
- 2. Die drei depends_on Conditions im Ueberblick
- 3. Healthcheck als Voraussetzung fuer service_healthy
- 4. Praxisbeispiel: PHP-FPM wartet auf MySQL
- 5. Praxisbeispiel: Redis und mehrere Abhaengigkeiten kombinieren
- 6. service_completed_successfully fuer Init-Container und Migrationen
- 7. Verhalten bei fehlgeschlagenen Healthchecks und Neustarts
- 8. Grenzen: was condition service_healthy nicht loest
- 9. service_healthy im Vergleich zu Wait-Skripten und Retry-Logik
- 10. Zusammenfassung
- 11. FAQ
1. Warum ein offener Port keine Betriebsbereitschaft bedeutet
Die klassische depends_on Direktive in Docker Compose garantiert nur eine Sache: Der abhaengige Service wird gestartet, nachdem der Container des referenzierten Service gestartet wurde. Sie garantiert ausdruecklich nicht, dass die Anwendung im referenzierten Container tatsaechlich bereit ist, Anfragen zu verarbeiten. Bei MySQL zum Beispiel liegen zwischen dem Start des Containerprozesses und der tatsaechlichen Bereitschaft, Verbindungen anzunehmen, oft mehrere Sekunden, in denen der Datenbankserver noch initialisiert, Tabellen prueft oder Recovery-Prozesse durchfuehrt.
Ein PHP-FPM Service, der unmittelbar nach dem simplen depends_on Start eine Verbindung zu MySQL aufbaut, laeuft in genau dieser Zeitspanne in einen Connection-Refused-Fehler, obwohl der MySQL Container formal bereits laeuft. Dieses Problem betraf lange Zeit fast jeden Docker Compose Stack mit Datenbank-Abhaengigkeit und fuehrte zu einer ganzen Generation von Workarounds, von Sleep-Befehlen in Entrypoint-Skripten bis zu externen Wait-for-it Tools.
depends_on mit condition service_healthy loest dieses Problem strukturell, indem es die Startreihenfolge nicht an den Containerstart, sondern an den tatsaechlichen Healthcheck-Status koppelt. Ein Service mit dieser Condition wird erst gestartet, wenn der Healthcheck des referenzierten Service den Status healthy zurueckmeldet, was die Notwendigkeit externer Wait-Mechanismen in den allermeisten Faellen vollstaendig entfaellt.
2. Die drei depends_on Conditions im Ueberblick
Docker Compose kennt in der Langform von depends_on drei unterschiedliche Bedingungen, die jeweils ein anderes Kriterium fuer die Startreihenfolge definieren. service_started ist die Standardbedingung und entspricht dem klassischen Verhalten: Der abhaengige Service startet, sobald der referenzierte Container gestartet wurde, unabhaengig vom internen Zustand der Anwendung. service_healthy erfordert, dass der referenzierte Service einen definierten Healthcheck besitzt und dieser den Status healthy meldet, bevor der abhaengige Service startet.
Die dritte Bedingung, service_completed_successfully, ist fuer einmalige Init-Prozesse gedacht und wartet, bis der referenzierte Container erfolgreich beendet wurde, also mit Exit-Code 0. Diese Bedingung eignet sich besonders fuer Datenbank-Migrationen oder Setup-Skripte, die einmalig laufen und danach beendet werden sollen, bevor die eigentliche Anwendung startet.
# compose.yaml — long-form depends_on with conditions
services:
php-fpm:
build: .
depends_on:
mysql:
condition: service_healthy
redis:
condition: service_healthy
db-migrate:
condition: service_completed_successfully
mysql:
image: mysql:8.0
healthcheck:
test: ["CMD", "mysqladmin", "ping", "-h", "localhost"]
interval: 5s
timeout: 3s
retries: 10
redis:
image: redis:7
healthcheck:
test: ["CMD", "redis-cli", "ping"]
interval: 5s
timeout: 3s
retries: 10
db-migrate:
build: .
command: ["php", "bin/console", "migrate"]
depends_on:
mysql:
condition: service_healthy
In diesem Beispiel wartet php-fpm gleichzeitig auf zwei service_healthy Bedingungen und eine service_completed_successfully Bedingung. Erst wenn MySQL und Redis als healthy gemeldet werden und der Migrations-Container erfolgreich durchgelaufen ist, startet php-fpm tatsaechlich. Docker Compose loest diese Abhaengigkeitskette automatisch auf und startet die Services in der korrekten Reihenfolge, ohne dass eine manuelle Wartezeit im Code der Anwendung noetig ist.
3. Healthcheck als Voraussetzung fuer service_healthy
Die Bedingung service_healthy funktioniert nur, wenn der referenzierte Service tatsaechlich einen healthcheck Block definiert. Ohne eigenen Healthcheck kennt Docker Compose fuer diesen Service gar keinen Gesundheitsstatus, und ein depends_on mit condition service_healthy auf einen Service ohne Healthcheck fuehrt zu einem Konfigurationsfehler beim Start. Der Healthcheck selbst besteht aus einem test Befehl, der innerhalb des Containers ausgefuehrt wird, sowie interval, timeout, retries und optional start_period.
Fuer Datenbanken ist der uebliche Healthcheck-Befehl das jeweilige eingebaute Ping-Tool: mysqladmin ping fuer MySQL, redis-cli ping fuer Redis, pg_isready fuer PostgreSQL. Diese Befehle pruefen nicht nur, ob der Prozess laeuft, sondern ob der Dienst tatsaechlich Verbindungen akzeptiert und grundlegende Anfragen beantwortet, was der entscheidende Unterschied zu einem reinen Port-Check ist.
4. Praxisbeispiel: PHP-FPM wartet auf MySQL
Fuer einen typischen PHP-FPM Service, der beim Start eine Datenbankverbindung aufbaut und ohne diese Verbindung nicht funktionsfaehig ist, ist condition service_healthy die naheliegende Loesung. Der MySQL Healthcheck meldet erst dann healthy, wenn mysqladmin ping erfolgreich antwortet, was in der Praxis bedeutet, dass der Server tatsaechlich bereit ist, Verbindungen zu akzeptieren, statt nur den Container-Prozess gestartet zu haben.
Ohne diese Absicherung muessten Entwickler entweder in der Anwendung selbst eine Retry-Logik mit exponentiellem Backoff einbauen, oder ein externes Wait-Skript im Entrypoint des PHP-FPM Containers ausfuehren, das per TCP-Verbindungsversuch prueft, ob MySQL bereits erreichbar ist. Beide Ansaetze funktionieren, sind aber zusaetzlicher Code, den condition service_healthy vollstaendig ueberfluessig macht, weil Docker Compose selbst diese Wartezeit uebernimmt.
# compose.yaml — PHP-FPM waits for a fully ready MySQL
services:
php-fpm:
build: ./docker/php
depends_on:
mysql:
condition: service_healthy
environment:
DB_HOST: mysql
mysql:
image: mysql:8.0
environment:
MYSQL_ROOT_PASSWORD_FILE: /run/secrets/mysql_root_password
MYSQL_DATABASE: magento
healthcheck:
test: ["CMD", "mysqladmin", "ping", "-h", "localhost", "-u", "root", "-p$$(cat /run/secrets/mysql_root_password)"]
interval: 5s
timeout: 3s
retries: 15
start_period: 20s
secrets:
- mysql_root_password
secrets:
mysql_root_password:
file: ./secrets/mysql_root_password.txt
5. Praxisbeispiel: Redis und mehrere Abhaengigkeiten kombinieren
In realen Stacks haengt ein Service selten von nur einer einzigen Ressource ab. Ein typischer Magento oder PHP-Anwendungsserver braucht sowohl eine Datenbankverbindung als auch einen Redis-Cache fuer Sessions und Full-Page-Cache. Docker Compose erlaubt es, mehrere depends_on Eintraege mit jeweils eigener condition zu kombinieren, und startet den abhaengigen Service erst, wenn alle referenzierten Bedingungen gleichzeitig erfuellt sind.
Wichtig dabei: Die verschiedenen Abhaengigkeiten werden parallel geprueft, nicht sequenziell. Docker Compose wartet nicht erst auf MySQL und beginnt danach mit der Pruefung von Redis, sondern startet die Healthchecks aller Abhaengigkeiten gleichzeitig und laesst den abhaengigen Service erst dann starten, wenn die langsamste Abhaengigkeit ebenfalls healthy ist. Das minimiert die Gesamtstartzeit des Stacks im Vergleich zu einer rein sequenziellen Wartelogik.
6. service_completed_successfully fuer Init-Container und Migrationen
Neben dem laufenden Betrieb gibt es in vielen Stacks einmalige Initialisierungsschritte, die vor dem eigentlichen Anwendungsstart abgeschlossen sein muessen: Datenbank-Migrationen, das Einspielen von Seed-Daten oder das Generieren statischer Assets. Fuer diese Faelle ist service_completed_successfully die passende Bedingung, weil sie nicht auf einen dauerhaften Healthcheck-Status wartet, sondern auf das erfolgreiche Beenden eines einmaligen Containers.
Ein haeufiges Muster in Magento-Projekten: Ein eigener migrate Service fuehrt bin/magento setup:upgrade aus und beendet sich danach mit Exit-Code 0. Der eigentliche php-fpm Service referenziert diesen migrate Service mit condition service_completed_successfully und startet erst, nachdem die Migration erfolgreich durchgelaufen ist. Schlaegt die Migration fehl und der Container beendet sich mit einem Fehler-Exit-Code, startet der abhaengige Service gar nicht erst, was verhindert, dass die Anwendung mit einem inkonsistenten Datenbankschema hochfaehrt.
# compose.yaml — one-shot migration gates the application start
services:
magento-setup:
build: .
command: ["php", "bin/magento", "setup:upgrade"]
depends_on:
mysql:
condition: service_healthy
php-fpm:
build: .
depends_on:
magento-setup:
condition: service_completed_successfully
mysql:
condition: service_healthy
7. Verhalten bei fehlgeschlagenen Healthchecks und Neustarts
Ein wichtiges Detail betrifft das Verhalten, wenn ein Service, dessen Healthcheck als Bedingung dient, nach dem Start unhealthy wird, etwa weil die Datenbank waehrend des Betriebs kurzzeitig ausfaellt. depends_on mit condition service_healthy wirkt ausschliesslich beim initialen Start des abhaengigen Services, nicht als laufende Ueberwachung waehrend des Betriebs. Wird MySQL nach dem Start von php-fpm unhealthy, fuehrt das nicht automatisch dazu, dass php-fpm gestoppt oder neu gestartet wird.
Fuer eine echte laufende Ueberwachung braucht es zusaetzliche Mechanismen, etwa eigene Healthchecks im abhaengigen Service selbst, die bei Verbindungsfehlern zur Datenbank ebenfalls unhealthy melden, kombiniert mit einer restart Policy wie unless-stopped oder on-failure, damit ein extern erkannter Ausfall zu einem automatischen Neustart fuehrt. condition service_healthy ist also ein Werkzeug fuer die korrekte Startreihenfolge, kein Werkzeug fuer laufendes Failover-Management.
8. Grenzen: was condition service_healthy nicht loest
Ein haeufiges Missverstaendnis ist, dass condition service_healthy die Anwendung selbst robuster gegen spaetere Verbindungsabbrueche macht. Das ist nicht der Fall. Die Bedingung sorgt lediglich dafuer, dass der Service beim allerersten Start nicht zu frueh gegen eine noch nicht bereite Abhaengigkeit laeuft. Verbindungsverluste waehrend des laufenden Betriebs, etwa durch einen MySQL-Neustart oder ein Netzwerkproblem, muessen weiterhin durch Retry-Logik in der Anwendung selbst abgefangen werden.
Eine weitere Grenze: condition service_healthy funktioniert nur innerhalb eines einzelnen docker compose up Aufrufs. Wird ein bereits laufender abhaengiger Service manuell mit docker compose restart neu gestartet, waehrend die referenzierte Abhaengigkeit kurzzeitig unhealthy ist, wartet Compose in diesem Fall nicht automatisch, weil restart ein anderer Befehlspfad als der initiale up ist. Fuer produktionsnahe Robustheit bleibt eine Kombination aus service_healthy fuer den Start und Retry-Logik in der Anwendung fuer den laufenden Betrieb die vollstaendige Loesung.
9. service_healthy im Vergleich zu Wait-Skripten und Retry-Logik
Vor der Einfuehrung von condition service_healthy loesten Teams das Startreihenfolge-Problem meist mit externen Wait-Skripten oder Retry-Logik direkt in der Anwendung. Beide Ansaetze funktionieren, bringen aber zusaetzlichen Code und zusaetzliche Komplexitaet mit, die condition service_healthy als eingebaute Compose-Funktion vollstaendig vermeidet.
| Ansatz | Ort der Logik | Zusaetzlicher Code | Deklarativ in compose.yaml |
|---|---|---|---|
| sleep im Entrypoint | Shell-Skript im Container | Ja, fragil und langsam | Nein |
| wait-for-it / dockerize | Externes Tool im Image | Ja, zusaetzliche Abhaengigkeit | Nein |
| Retry-Logik in der Anwendung | Anwendungscode | Ja, dauerhaft zu pflegen | Nein |
| depends_on condition service_healthy | compose.yaml | Nein | Ja |
Der entscheidende Vorteil von condition service_healthy liegt darin, dass die Startreihenfolge Teil der versionierten compose.yaml wird, statt in Shell-Skripten oder Anwendungscode versteckt zu sein. Retry-Logik in der Anwendung bleibt sinnvoll fuer den laufenden Betrieb, ist aber fuer das reine Startreihenfolge-Problem die aufwendigere Loesung im Vergleich zur deklarativen Compose-Bedingung.
Mironsoft
Docker-Compose-Orchestrierung und Healthcheck-Design
Stacks, die beim Start nicht mehr in Connection Refused laufen?
Wir richten condition service_healthy fuer eure Multi-Service-Stacks ein, entfernen fragile Wait-Skripte und sorgen fuer eine zuverlaessige Startreihenfolge zwischen MySQL, Redis und PHP-FPM.
Healthcheck-Design
Passende test Befehle und Timings fuer Datenbanken und Caches definieren
Startreihenfolge
depends_on mit service_healthy und service_completed_successfully strukturieren
Magento-Migrationen
setup:upgrade als Gate vor dem Start des eigentlichen Anwendungscontainers
10. Zusammenfassung
depends_on mit condition service_healthy ersetzt die Annahme, dass ein gestarteter Container bereit ist, durch die Ueberpruefung eines tatsaechlichen Healthcheck-Status. Statt sich auf einen offenen Port zu verlassen, wartet ein abhaengiger Service erst dann, wenn MySQL, Redis oder ein anderer Dienst per Healthcheck als healthy gemeldet wird. Die Bedingung service_completed_successfully ergaenzt dieses Muster fuer einmalige Init-Prozesse wie Datenbank-Migrationen, die vor dem eigentlichen Anwendungsstart erfolgreich durchlaufen sein muessen.
Wichtig bleibt, dass condition service_healthy nur den initialen Start absichert, nicht den laufenden Betrieb. Fuer echte Ausfallsicherheit waehrend der Laufzeit braucht die Anwendung weiterhin eigene Retry-Logik. Im Vergleich zu externen Wait-Skripten oder manuell eingebauter Sleep-Logik ist condition service_healthy trotzdem die deutlich sauberere, deklarative Loesung fuer das Startreihenfolge-Problem in Docker Compose Stacks.
depends_on mit condition service_healthy — Das Wichtigste auf einen Blick
service_started
Standardverhalten, wartet nur auf den Containerstart, nicht auf die Anwendungsbereitschaft.
service_healthy
Wartet auf einen erfolgreichen Healthcheck des referenzierten Service, erfordert einen definierten healthcheck Block.
service_completed_successfully
Wartet auf erfolgreiches Beenden eines einmaligen Containers, ideal fuer Migrationen und Setup-Skripte.
Grenze
Wirkt nur beim initialen Start, keine laufende Ueberwachung waehrend des Betriebs.