Introduction
Docker Compose orchestrates multi-container applications, managing startup order through the depends_on directive. However, depends_on alone only guarantees that the dependency container has started—not that it's ready to accept connections or process requests. A database container might be running while still initializing its data files, rejecting connections until startup completes. An API server might listen on its port before finishing configuration loading.
Health checks bridge this gap by defining what "ready" actually means for each service. When depends_on includes a condition: service_healthy, Docker Compose waits until the dependency's health check passes before starting the dependent service. This ensures services connect to dependencies that are truly ready, not just container processes that exist.
When health checks fail or are misconfigured, dependent services either wait indefinitely or crash repeatedly attempting connections to unready dependencies. Understanding health check mechanics—the test command, intervals, timeouts, retries, and start periods—is essential for configuring reliable startup ordering in Docker Compose applications.
Symptoms
When Docker Compose dependency health checks fail, you will observe these symptoms:
docker compose upshows a service stuck waiting for dependency to become healthy- Dependency containers show
health: startingorunhealthystatus indefinitely - Dependent services crash with connection refused or timeout errors
- Startup succeeds on fast machines but fails on slower hardware or fresh environments
- Services restart repeatedly attempting to connect to dependencies
- Manual container startup works but
depends_onwith health condition fails - Health check logs show timeout or command exit code failures
Common Docker Compose output showing dependency failure:
``` [+] Running 4/5 ⠿ Container api-server Waiting for service_healthy condition from database ⠿ Container database Waiting (health: starting) ⠿ Container redis Healthy ⠿ Container nginx Running
Error: dependency "database" failed to become healthy within 300 seconds ```
Docker inspect showing unhealthy status:
```bash docker inspect --format='{{.State.Health.Status}}' database # Output: unhealthy
docker inspect --format='{{json .State.Health.Log}}' database | jq . # Output: { "Output": "Health check exceeded timeout (5s)", "ExitCode": 1, "Start": "2026-01-15T10:30:00Z", "End": "2026-01-15T10:30:05Z" } ```
Application logs showing connection failures:
api-server error: Connection refused to database:5432
api-server error: Unable to connect to PostgreSQL: connection timeout
api-server restarting due to dependency unavailableCommon Causes
Several factors cause Docker Compose dependency health check failures:
- 1.Health check command incorrect: The test command targets wrong port, path, or uses wrong protocol. A curl to
/healthon port 8080 fails when the app listens on 3000 or the endpoint is/api/health. - 2.Health check timeout too short: The command takes longer to execute than the configured timeout. Complex readiness checks involving network calls or database queries may exceed simple timeouts.
- 3.Start period insufficient: Slow-starting services need adequate start period before health checks begin counting failures. PostgreSQL initialization, Elasticsearch cluster formation, or large database migrations need generous start periods.
- 4.Service binds to localhost only: The health check runs from inside the container but the service binds to
127.0.0.1, making external health check requests fail. - 5.Retries exhausted before service ready: Too few retries combined with short intervals causes health checks to fail before slow services complete initialization.
- 6.Health check marks healthy prematurely: The check passes too early, before the service is truly ready. An HTTP 200 from a loading page doesn't mean the app can handle requests.
- 7.Network or DNS resolution delays: Container networking initialization takes time, causing health check commands to fail initially due to DNS or network unavailability.
- 8.Resource constraints slow startup: CPU or memory limits cause services to start slower than health check configuration expects.
Step-by-Step Fix
Follow these steps to diagnose and resolve Docker Compose dependency health failures:
Step 1: Identify the failing dependency
Check which service is unhealthy:
```bash # View container health status docker compose ps
# Output showing unhealthy service NAME STATUS database Up 30s (unhealthy) api-server Waiting (dependency unhealthy) redis Up 45s (healthy)
# Get detailed health status docker inspect --format='{{.State.Health.Status}}' database docker inspect --format='{{.State.Health}}' database | jq . ```
Step 2: Review health check configuration
Examine the current health check definition:
```bash # View compose file health check grep -A 10 "healthcheck:" docker-compose.yml
# Or read full service definition cat docker-compose.yml | grep -A 30 "database:" ```
Example health check configuration:
services:
database:
image: postgres:15
healthcheck:
test: ["CMD-SHELL", "pg_isready -U postgres"]
interval: 10s
timeout: 5s
retries: 5
start_period: 30sStep 3: Run health check command manually
Test the health check inside the container:
```bash # Execute health check command manually docker exec database pg_isready -U postgres
# If command fails, check application status docker exec database pg_isready -U postgres -h localhost -p 5432
# For HTTP-based health checks docker exec api-server curl -f http://localhost:8080/health
# Check if port is listening docker exec database netstat -tlnp | grep 5432 docker exec api-server ss -tlnp | grep 8080 ```
Step 4: Check container logs for startup issues
Examine why the service isn't becoming healthy:
```bash # View service logs docker compose logs database
# Follow logs during startup docker compose logs -f database
# Check for specific startup messages docker compose logs database | grep -E "ready|listening|started|error|fail" ```
Database startup logs showing initialization:
database logs:
PostgreSQL init process in progress...
creating database directories...
initializing database...
syncing data to disk...
database system is ready to accept connectionsStep 5: Adjust health check timing
Update the health check configuration for realistic startup times:
services:
database:
image: postgres:15
healthcheck:
test: ["CMD-SHELL", "pg_isready -U postgres -h localhost -p 5432"]
interval: 10s # Check every 10 seconds
timeout: 10s # Allow 10 seconds for check to complete
retries: 10 # Try 10 times before marking unhealthy
start_period: 60s # Wait 60s before counting failuresFor slow-starting services like Elasticsearch:
services:
elasticsearch:
image: elasticsearch:8.0
healthcheck:
test: ["CMD-SHELL", "curl -f http://localhost:9200/_cluster/health?wait_for_status=yellow"]
interval: 10s
timeout: 30s # Cluster health check can be slow
retries: 15 # Many retries for cluster formation
start_period: 120s # Allow 2 minutes for node startupStep 6: Fix incorrect health check commands
Correct the test command for your service:
```yaml # PostgreSQL - use pg_isready with correct parameters healthcheck: test: ["CMD-SHELL", "pg_isready -U $$POSTGRES_USER -h localhost"]
# MySQL - use mysqladmin ping healthcheck: test: ["CMD-SHELL", "mysqladmin ping -h localhost -u $$MYSQL_USER -p$$MYSQL_PASSWORD"]
# Redis - use redis-cli ping healthcheck: test: ["CMD", "redis-cli", "ping"]
# HTTP services - curl with fail flag healthcheck: test: ["CMD", "curl", "-f", "http://localhost:3000/api/health"]
# For services without curl installed healthcheck: test: ["CMD-SHELL", "wget --no-verbose --tries=1 --spider http://localhost:3000/health || exit 1"] ```
Step 7: Verify network binding
Ensure services listen on correct interfaces:
```bash # Check what interface service binds to docker exec database netstat -tlnp
# If binding to 127.0.0.1, it may not respond to health checks from container network # For PostgreSQL, check postgresql.conf docker exec database cat /var/lib/postgresql/data/postgresql.conf | grep listen_addresses
# Should be: listen_addresses = '*' # Or localhost for container-local ```
If service binds to localhost only, adjust health check to use localhost:
healthcheck:
test: ["CMD-SHELL", "pg_isready -h localhost -U postgres"]Step 8: Configure depends_on properly
Set dependency conditions correctly:
services:
api-server:
depends_on:
database:
condition: service_healthy # Wait for health check to pass
redis:
condition: service_started # Only wait for container startDependency condition options:
- service_started: Container has started (default behavior)
- service_healthy: Container health check passes
- service_completed_successfully: Container exits with success (for one-time setup)
Step 9: Test with adjusted configuration
Restart services with new health checks:
```bash # Stop and remove containers docker compose down
# Start with fresh state docker compose up -d
# Watch startup progress docker compose up --detach && docker compose logs -f
# Check health status docker compose ps docker inspect --format='{{.State.Health.Status}}' database ```
Verification
After fixing health check configuration, verify services start correctly:
```bash # Run full stack startup docker compose up -d
# Check all services healthy docker compose ps
# Expected output NAME STATUS database Up 60s (healthy) api-server Up 30s (healthy) redis Up 45s (healthy) nginx Up 45s (healthy)
# Test service connectivity docker exec api-server curl -f http://database:5432 || echo "Cannot connect" docker exec api-server nc -zv redis 6379
# Verify application works curl http://localhost:8080/api/health ```
Verify health check timing is appropriate:
```bash # Measure actual startup time docker compose down time docker compose up -d --wait
# Check health check log entries docker inspect --format='{{range .State.Health.Log}}{{.Start}} - {{.End}}: {{.ExitCode}}{{println}}{{end}}' database ```
Prevention
To prevent Docker Compose dependency health failures:
- 1.Test health checks manually: Always execute health check commands inside containers before relying on them.
docker exec <container> <health-check-command>
# Must return exit code 0 for healthy, non-zero for unhealthy- 1.Set generous start periods: Give slow services adequate time before health checks count failures.
healthcheck:
start_period: 60s # Minimum for databases
start_period: 120s # For Elasticsearch, Kafka clusters- 1.Use official image health checks when available: Many official Docker images include HEALTHCHECK instructions.
# postgres:15 already has HEALTHCHECK built-in
# Override only if you need custom timing
services:
database:
image: postgres:15
# Inherits healthcheck, adjust timing if needed
healthcheck:
start_period: 60s- 1.Validate with docker compose config: Check compose file syntax before running.
docker compose config
# Validates YAML and interpolates variables- 1.Document expected startup times: Record how long each service takes to start for future tuning.
## Service Startup Times
- PostgreSQL: 30-60s (initialization)
- Elasticsearch: 90-120s (cluster formation)
- API Server: 10-20s (depends on database)- 1.Use docker compose wait: Enable waiting for all services to be healthy.
```bash # Wait for all services with health checks to pass docker compose up --wait
# Set max wait time docker compose up --wait --timeout 300 ```
- 1.Monitor health check frequency: Don't set intervals too aggressive; excessive checks waste resources.
healthcheck:
interval: 10s # Reasonable for most services
# Avoid: interval: 1s (too frequent)Related Articles
- [Fix docker build cache invalidated unnecessary layers Issue in Docker-Errors](docker-build-cache-invalidated-unnecessary-layers)
- [Fix Docker Build Cache Invalidation Optimization Issue in Docker](docker-build-cache-invalidation-optimization)
- [Fix docker build context slow large files Issue in Docker-Errors](docker-build-context-slow-large-files)
- [Fix docker build multi stage copy from not found Issue in Docker-Errors](docker-build-multi-stage-copy-from-not-found)
- [Fix docker buildkit export local tar layer missing Issue in Docker-Errors](docker-buildkit-export-local-tar-layer-missing)
<script type="application/ld+json"> { "@context": "https://schema.org", "@type": "TechArticle", "headline": "Docker Compose Service Failed Because a Dependency Never Became Healthy", "description": "Resolve Docker Compose dependency startup failures by inspecting health checks, testing readiness commands inside containers, and tuning start period and dependency conditions.", "url": "https://www.fixwikihub.com/docker-compose-service-failed-dependency", "publisher": { "@type": "Organization", "name": "FixWikiHub", "url": "https://www.fixwikihub.com" }, "author": { "@type": "Person", "name": "FixWikiHub Editorial Team" }, "datePublished": "2026-01-23T03:03:58.191Z", "dateModified": "2026-01-23T03:03:58.191Z" } </script>