dependency failed to start: container X is unhealthy
ID: docker/compose-depends-on-unhealthy
Version Compatibility
| Version | Status | Introduced | Deprecated | Notes |
|---|---|---|---|---|
| 27 | active | — | — | — |
Root Cause
The dependency service's healthcheck is failing or missing. Docker Compose v2 requires explicit healthchecks with 'depends_on: condition: service_healthy'. The most common cause is a missing or incorrect healthcheck definition.
genericWorkarounds
-
94% success Add a proper healthcheck to the dependency service
In docker-compose.yml, add a healthcheck to the dependency. For PostgreSQL: healthcheck: { test: ['CMD-SHELL', 'pg_isready -U postgres'], interval: 5s, timeout: 5s, retries: 5 }. For MySQL: test: ['CMD', 'mysqladmin', 'ping', '-h', 'localhost']. For Redis: test: ['CMD', 'redis-cli', 'ping']. Then use depends_on: { db: { condition: service_healthy } }.Sources: https://docs.docker.com/compose/compose-file/05-services/#healthcheck
-
88% success Use wait-for-it.sh or dockerize as entrypoint wrapper
Add wait-for-it.sh to your image and use as entrypoint: entrypoint: ['./wait-for-it.sh', 'db:5432', '--', 'node', 'server.js']. This polls the port until the dependency is accepting connections before starting the application.
-
90% success Ensure healthcheck tests actual readiness, not just port availability
A TCP port check (nc -z) returns true before the database is ready to accept queries. Use service-specific readiness commands: pg_isready for PostgreSQL, mysqladmin ping for MySQL, redis-cli ping for Redis, curl localhost:9200/_cluster/health for Elasticsearch.
Dead Ends
Common approaches that don't work:
-
Adding sleep commands in entrypoint scripts as a wait mechanism
75% fail
Sleep-based waits are brittle and slow. They do not actually check service health and fail silently if the dependency takes longer than the sleep duration. Docker Compose v2 has native health-check-based waiting.
-
Removing depends_on to let services start in parallel
88% fail
Services start simultaneously with no ordering, causing 'connection refused' errors when the application starts before the database is ready. This makes the problem worse by trading a clear error for intermittent failures.
-
Setting restart: always to keep retrying the dependent service
82% fail
Creates an infinite restart loop: service fails → restarts → fails again. The dependency never becomes healthy just because the dependent restarts. Consumes resources and fills logs with repeated failures.