This error occurs when a container that another service depends on fails its health check and is marked as 'unhealthy'. Docker Compose stops waiting and prevents the dependent service from starting, requiring you to fix the unhealthy dependency or adjust health check timing.
The "dependency failed to start" error in Docker Compose indicates that a service your application depends on could not reach a healthy state within the allowed time. When you use `depends_on` with `condition: service_healthy`, Docker Compose waits for the dependency's health check to pass before starting your service. If the dependency exhausts all health check retries while still unhealthy, Docker Compose reports "dependency failed to start: container [name] is unhealthy" and stops the startup process entirely. This is expected behavior introduced in Docker Compose v2.22.0+ to fail fast rather than leave you with partially running stacks. The root cause is typically one of two things: either the dependency service itself has a problem (configuration error, missing files, resource constraints), or the health check parameters are too aggressive for the service's actual startup time.
First, identify why the dependency is unhealthy by checking its logs and health check status:
# View container logs
docker compose logs <service-name>
# Check health check status in detail
docker inspect --format='{{json .State.Health}}' <container-name> | jq
# Or without jq
docker inspect <container-name> | grep -A 20 "Health"Look for:
- Application startup errors
- Missing configuration or environment variables
- Permission denied errors
- Connection failures to external services
The health check output shows the exit codes and output of each health check attempt, which reveals exactly why it's failing.
The start_period defines how long Docker waits before counting failed health checks against the retry limit. If your service takes time to initialize, increase this value:
services:
db:
image: postgres:15
healthcheck:
test: ["CMD-SHELL", "pg_isready -U postgres"]
interval: 10s
timeout: 5s
retries: 5
start_period: 60s # Increased from default 0sRecommended values by service type:
- Simple databases (Redis, Memcached): 10-20s
- PostgreSQL/MySQL: 30-60s
- Elasticsearch/MongoDB: 60-120s
- Applications with migrations: 60-180s
During the start_period, failed health checks don't count toward the retry limit, giving slower services time to initialize.
Test the health check command manually inside the container to confirm it works:
# Start just the dependency service
docker compose up -d db
# Execute the health check command manually
docker compose exec db pg_isready -U postgres
# For HTTP health checks
docker compose exec app curl -f http://localhost:8080/healthCommon health check commands:
# PostgreSQL
test: ["CMD-SHELL", "pg_isready -U ${POSTGRES_USER:-postgres}"]
# MySQL
test: ["CMD", "mysqladmin", "ping", "-h", "localhost", "-u", "root", "-p${MYSQL_ROOT_PASSWORD}"]
# Redis
test: ["CMD", "redis-cli", "ping"]
# Generic HTTP
test: ["CMD", "curl", "-f", "http://localhost:8080/health"]
# MongoDB
test: ["CMD", "mongosh", "--eval", "db.adminCommand('ping')"]If the command fails, check that:
- Required tools (curl, pg_isready) are installed in the image
- The service is listening on the expected port
- Authentication credentials are correct
Address the underlying problems that prevent the service from becoming healthy:
Database initialization is slow:
services:
db:
image: postgres:15
volumes:
- ./init.sql:/docker-entrypoint-initdb.d/init.sql
healthcheck:
start_period: 120s # Allow time for init scriptsMissing environment variables:
services:
app:
environment:
- DATABASE_URL=postgres://user:pass@db:5432/mydb
- REDIS_URL=redis://redis:6379
env_file:
- .env # Make sure this file existsPermission issues with volumes:
# Fix ownership on Linux
sudo chown -R 1000:1000 ./data
# Or use a named volume instead
volumes:
db_data:Resource constraints:
services:
db:
deploy:
resources:
limits:
memory: 1G
reservations:
memory: 512MFine-tune the health check parameters to give services more time:
services:
db:
image: postgres:15
healthcheck:
test: ["CMD-SHELL", "pg_isready -U postgres"]
interval: 5s # Check every 5 seconds
timeout: 10s # Wait 10s for response
retries: 10 # Try 10 times after start_period
start_period: 30s # 30s grace periodUnderstanding the timing:
- start_period: Grace period before retries count (30s)
- interval: Time between checks (5s)
- retries: Max failures after start_period (10)
- timeout: How long to wait for each check (10s)
Total wait time = start_period + (retries * interval) = 30s + (10 * 5s) = 80s
Increase these values for services that need more initialization time.
If you need to get the stack running while debugging, temporarily use service_started instead of service_healthy:
services:
app:
depends_on:
db:
condition: service_started # Only waits for container to start
# condition: service_healthy # Commented out for debugging
db:
image: postgres:15
# healthcheck still defined for monitoring
healthcheck:
test: ["CMD-SHELL", "pg_isready -U postgres"]
interval: 10s
timeout: 5s
retries: 5Important: This is a workaround, not a fix. Your application should handle connection retries gracefully. Consider adding retry logic:
services:
app:
command: ["./wait-for-it.sh", "db:5432", "--timeout=60", "--", "npm", "start"]Or implement connection retry logic in your application code.
Behavior change in Docker Compose v2.22.0+: Earlier versions would continue waiting indefinitely if a dependency became unhealthy but the container kept running. Newer versions fail fast when health checks exhaust their retries, which is why you may see this error appear after Docker updates.
Using restart policies with health checks: Combine health checks with restart policies to auto-recover from transient failures:
services:
db:
restart: unless-stopped
healthcheck:
test: ["CMD-SHELL", "pg_isready -U postgres"]
interval: 10s
retries: 3
start_period: 30sThe `required: false` option: Make a dependency optional so your service starts even if the dependency fails:
depends_on:
cache:
condition: service_healthy
required: false # App starts even if cache is unhealthyDebugging health checks in CI/CD: Add verbose health check output for CI environments:
healthcheck:
test: ["CMD-SHELL", "pg_isready -U postgres || (echo 'Health check failed' && exit 1)"]Docker events for monitoring: Watch health transitions in real-time:
docker events --filter 'type=container' --filter 'event=health_status'Multi-stage dependencies: For complex startup orders, consider using init containers or startup scripts rather than relying solely on health checks:
services:
db-init:
image: postgres:15
command: ["psql", "-h", "db", "-U", "postgres", "-c", "SELECT 1"]
depends_on:
db:
condition: service_healthy
app:
depends_on:
db-init:
condition: service_completed_successfullyimage operating system "linux" cannot be used on this platform
How to fix 'image operating system linux cannot be used on this platform' in Docker
manifest unknown: manifest unknown
How to fix 'manifest unknown' in Docker
cannot open '/etc/passwd': Permission denied
How to fix 'cannot open: Permission denied' in Docker
Error response from daemon: failed to create the ipvlan port
How to fix 'failed to create the ipvlan port' in Docker
toomanyrequests: Rate exceeded for anonymous users
How to fix 'Rate exceeded for anonymous users' in Docker Hub