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 up shows a service stuck waiting for dependency to become healthy
  • Dependency containers show health: starting or unhealthy status 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_on with 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:

bash
api-server error: Connection refused to database:5432
api-server error: Unable to connect to PostgreSQL: connection timeout
api-server restarting due to dependency unavailable

Common Causes

Several factors cause Docker Compose dependency health check failures:

  1. 1.Health check command incorrect: The test command targets wrong port, path, or uses wrong protocol. A curl to /health on port 8080 fails when the app listens on 3000 or the endpoint is /api/health.
  2. 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. 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. 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. 5.Retries exhausted before service ready: Too few retries combined with short intervals causes health checks to fail before slow services complete initialization.
  6. 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. 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. 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:

yaml
services:
  database:
    image: postgres:15
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U postgres"]
      interval: 10s
      timeout: 5s
      retries: 5
      start_period: 30s

Step 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:

bash
database logs:
PostgreSQL init process in progress...
creating database directories...
initializing database...
syncing data to disk...
database system is ready to accept connections

Step 5: Adjust health check timing

Update the health check configuration for realistic startup times:

yaml
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 failures

For slow-starting services like Elasticsearch:

yaml
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 startup

Step 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:

yaml
healthcheck:
  test: ["CMD-SHELL", "pg_isready -h localhost -U postgres"]

Step 8: Configure depends_on properly

Set dependency conditions correctly:

yaml
services:
  api-server:
    depends_on:
      database:
        condition: service_healthy  # Wait for health check to pass
      redis:
        condition: service_started   # Only wait for container start

Dependency 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. 1.Test health checks manually: Always execute health check commands inside containers before relying on them.
bash
docker exec <container> <health-check-command>
# Must return exit code 0 for healthy, non-zero for unhealthy
  1. 1.Set generous start periods: Give slow services adequate time before health checks count failures.
yaml
healthcheck:
  start_period: 60s   # Minimum for databases
  start_period: 120s  # For Elasticsearch, Kafka clusters
  1. 1.Use official image health checks when available: Many official Docker images include HEALTHCHECK instructions.
yaml
# 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. 1.Validate with docker compose config: Check compose file syntax before running.
bash
docker compose config
# Validates YAML and interpolates variables
  1. 1.Document expected startup times: Record how long each service takes to start for future tuning.
markdown
## Service Startup Times
- PostgreSQL: 30-60s (initialization)
- Elasticsearch: 90-120s (cluster formation)
- API Server: 10-20s (depends on database)
  1. 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. 1.Monitor health check frequency: Don't set intervals too aggressive; excessive checks waste resources.
yaml
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>