LEARN · DEBUGGING GUIDE

Docker Compose depends_on Not Waiting – Why Your App Starts Before the Database Is Ready

depends_on in Docker Compose only guarantees the dependency container has started, not that the service inside is ready to accept connections. Your app crashes because it connects to a database that's still initializing.

IntermediateDocker7 min read

What this usually means

Docker Compose's depends_on option checks container lifecycle states (created, running), not the internal service health. A PostgreSQL container is 'running' as soon as the postmaster process starts, but the database may still be recovering, applying WAL, or accepting connections only after initialization scripts finish. The dependent container (your app) starts immediately and tries to connect before the database is ready. This race condition is the root cause. The fix requires combining depends_on with condition: service_healthy and proper healthcheck configurations, or using a wait script inside the container.

( 01 )Fast diagnosis

The first ten minutes — establish facts before touching code.

  • 1Run docker compose logs <dependent-service> to see the exact connection error and timestamps
  • 2Check docker compose ps --all to confirm the dependency container status is 'Up' (not 'healthy' unless configured)
  • 3Inspect the dependency container logs: docker compose logs <dependency> | grep -i 'ready|listening|started' to see when the service is actually ready
  • 4Add a healthcheck to the dependency service in docker-compose.yml (e.g., for PostgreSQL) and set condition: service_healthy in depends_on
  • 5Alternatively, start the dependency service alone, wait 10 seconds, then start the dependent service manually to confirm readiness timing
  • 6Run docker compose events to observe container lifecycle events and see the order of start/stop signals
( 02 )Where to look

The specific files, logs, configs, and dashboards that usually own this bug.

  • searchdocker-compose.yml: the depends_on section of the dependent service
  • searchdocker-compose.yml: the healthcheck section of the dependency service (missing or incorrect)
  • searchApplication startup scripts or entrypoint: any script that attempts to connect to dependencies at startup
  • searchDependency service logs: docker compose logs <dependency> to see when it reports readiness
  • searchDependent service logs: docker compose logs <dependent> for connection errors
  • searchDocker Compose version: docker compose version (old versions <1.27 had a different depends_on behavior)
  • searchCI/CD pipeline logs: if running in a test environment, check for timeout or race conditions
( 03 )Common root causes

Practical causes, not theory. These are the things you will actually find.

  • warningUsing depends_on without condition: service_healthy and no healthcheck defined on the dependency
  • warninghealthcheck defined but with too short interval or retries, causing false positive before service is ready
  • warningHealthcheck command incorrect for the service (e.g., using pg_isready but not checking for a specific database)
  • warningDependency service has initialization scripts that take longer than the healthcheck timeout
  • warningDocker Compose version < 2.0 where condition: service_healthy is not supported
  • warningMisconfigured restart policy causing containers to restart before healthchecks complete
  • warningUsing depends_on in a service that also depends on another, creating a chain where one link fails
( 04 )Fix patterns

Concrete fix directions. Pick the one that matches your root cause.

  • buildAdd a proper healthcheck to the dependency service (e.g., for PostgreSQL: test: ["CMD-SHELL", "pg_isready -U postgres"])
  • buildAdd condition: service_healthy under depends_on in the dependent service
  • buildUse a wait-for-it script as the entrypoint of the dependent service to poll the dependency until ready
  • buildIncrease healthcheck start_period to account for initialization time
  • buildUse a wrapper script that retries connections with exponential backoff before starting the app
  • buildFor older Compose versions, use external scripts or tools like dockerize to wait for dependencies
( 05 )How to verify

A fix you cannot prove is a guess. Close the loop.

  • verifiedRun docker compose up --build and check that the dependent service now starts after the dependency reports healthy
  • verifiedCheck docker compose ps to verify both services are 'Up (healthy)'
  • verifiedInspect logs: docker compose logs <dependent> should show no connection errors and app started successfully
  • verifiedTest restart: docker compose restart <dependent> should still work because dependency is already healthy
  • verifiedSimulate delayed readiness by adding a sleep in the dependency's entrypoint and confirm the wait works
  • verifiedRun docker compose down && docker compose up again to ensure consistency across fresh starts
( 06 )Mistakes to avoid

Things that make this bug worse or harder to find.

  • warningAdding depends_on without healthcheck and condition: service_healthy (it only checks container start)
  • warningSetting healthcheck with a command that doesn't actually verify readiness (e.g., pg_isready without user/db)
  • warningUsing condition: service_started instead of condition: service_healthy (the default, no help)
  • warningRelying on sleep in entrypoint scripts instead of proper healthcheck (brittle, varies by environment)
  • warningForgetting to update the healthcheck when dependency image version changes (e.g., new defaults)
  • warningOverlooking that depends_on also affects shutdown order; containers may stop in wrong order causing data loss
( 07 )War story

Payment API crashes on deploy because Redis isn't ready

Backend EngineerDocker Compose 2.15, Node.js 18, PostgreSQL 15, Redis 7

Timeline

  1. 09:15Deploy new payment API version to staging using docker compose up --build
  2. 09:16Payment API container exits with code 1; logs show 'Error: connect ECONNREFUSED 127.0.0.1:6379'
  3. 09:17Redis logs show 'Ready to accept connections' 3 seconds after the API crashed
  4. 09:18Check docker-compose.yml: depends_on only on redis, no healthcheck
  5. 09:20Add healthcheck to Redis: test: ["CMD", "redis-cli", "ping"] and condition: service_healthy in depends_on
  6. 09:22Redeploy: this time Redis shows 'healthy' before API starts; API connects successfully
  7. 09:25Run integration tests: all pass; deploy to production with the fix

We had a routine deployment of our payment API to staging. The API is a Node.js service that reads from Redis for rate limiting and writes to PostgreSQL for transactions. The docker-compose.yml had depends_on set for both redis and postgres, but no healthchecks. After the build, the API container started immediately and tried to connect to Redis, which was still starting up. The connection refused error caused the API to crash. The restart: always policy made it loop infinitely, flooding logs with the same error.

I checked the Redis logs and saw that Redis became ready to accept connections about 3 seconds after the API first attempted. The race condition was clear: depends_on only waited for the container to be 'running', not for Redis to be 'ready'. I added a healthcheck to the Redis service: test: ["CMD", "redis-cli", "ping"] with a 5-second interval and 3 retries. Then I added condition: service_healthy to the depends_on for the API service.

After redeploying, I saw in docker compose ps that Redis showed 'Up (healthy)' before the API started. The API connected successfully and the service came up without errors. We also added healthchecks to PostgreSQL for good measure. Since then, we've made it a standard practice to always define healthchecks for any service that has depends_on with condition: service_healthy.

Root cause

depends_on without condition: service_healthy only waits for container start, not service readiness.

The fix

Added healthcheck to Redis and set condition: service_healthy in the API service's depends_on.

The lesson

Always pair depends_on with healthchecks and condition: service_healthy, or use wait scripts for readiness.

( 08 )How depends_on Actually Works

The depends_on option in Docker Compose controls the order of service startup and shutdown. Without conditions, it simply ensures the dependency container is started before the dependent container. However, 'started' means the container's main process is running, not that the service inside is ready to accept connections. This is a common pitfall.

Since Docker Compose v2, you can add condition: service_healthy to depends_on, but this requires the dependency to have a healthcheck defined. The healthcheck must be a command that returns 0 when the service is ready. Docker Compose will then wait until the dependency's health status is 'healthy' before starting the dependent service. Note that condition: service_started is the default and does not wait for readiness.

( 09 )Crafting Effective Healthchecks

A healthcheck should test the actual readiness of the service, not just that the process is running. For PostgreSQL, use pg_isready with the appropriate user and database. For Redis, redis-cli ping is sufficient. For web services, curl against a health endpoint that checks dependencies.

Key parameters: interval (how often to check), timeout (max time for a check), retries (consecutive failures before marking unhealthy), start_period (initial grace period before healthchecks begin). Set start_period long enough to cover initialization (e.g., 30s for a database). If the healthcheck fails during start_period, it does not count toward retries.

( 10 )Alternative: Wait Scripts in Entrypoint

If you cannot modify the docker-compose.yml (e.g., in a shared environment), you can embed a wait script in the dependent container's entrypoint. Tools like wait-for-it (bash), dockerize, or a simple Node.js script can poll the dependency until it's ready before starting the main process.

Example: ./wait-for-it.sh db:5432 -- node app.js. This script loops until the TCP connection succeeds. However, this approach still has a race if the dependency accepts connections but isn't fully ready. Combining both healthcheck and wait script is robust.

( 11 )Shutdown Order and depends_on

depends_on also affects shutdown order: dependencies stop after the dependent service. This is important for cleanup. If your dependent service writes to a database, you want the database to stay up until the dependent finishes. Without depends_on, containers may stop in any order, potentially causing data loss.

To control shutdown order, you can also use depends_on with condition: service_started. But for readiness, you need healthchecks. In complex stacks, consider using Docker Compose profiles or separate compose files for different environments.

( 12 )Debugging with Compose Events and Timestamps

Use docker compose events to see real-time container lifecycle events. Filter by type: docker compose events --json | grep -E 'start|die|health_status'. This shows the exact order and timing of container starts, stops, and health transitions.

For precise timing, add timestamps to logs: docker compose logs --timestamps. Compare when the dependency reports 'ready' vs when the dependent crashes. This confirms the race condition.

Frequently asked questions

Does depends_on guarantee that my database is ready for connections?

No. depends_on only guarantees the container has started. The service inside (e.g., PostgreSQL) may still be initializing. To guarantee readiness, you must combine depends_on with condition: service_healthy and a proper healthcheck on the dependency.

What is the difference between condition: service_started and condition: service_healthy?

condition: service_started (the default) waits only for the container to be in the 'running' state. condition: service_healthy waits until the container's healthcheck reports 'healthy'. The latter is required to ensure the service is ready to accept connections.

My healthcheck passes but the service still isn't ready. What could be wrong?

Your healthcheck command might not be testing the right thing. For example, pg_isready without a database name might return success even if the specific database isn't created yet. Also, check the start_period: if the healthcheck starts too early and fails enough times, the service may be marked unhealthy. Ensure the healthcheck command truly reflects readiness.

Can I use depends_on without healthchecks in older Docker Compose versions?

In Docker Compose v1 (version < 1.27), depends_on did not support conditions at all. You had to use external tools like wait-for-it or dockerize. In v1.27+, condition: service_healthy was introduced. Check your version with docker compose version. For v2+, healthchecks are the recommended approach.

What if I have a chain of dependencies, e.g., app depends on API depends on DB?

Docker Compose respects the dependency chain. If app depends_on api with condition: service_healthy, and api depends_on db with condition: service_healthy, then app will wait for api to be healthy, which in turn waits for db to be healthy. Ensure all services in the chain have healthchecks defined and conditions set.