$docker.recipes
·10 min read·Updated January 2026

Docker Compose Health Checks and Startup Order Done Right

How to use health checks and depends_on conditions to ensure your services start in the correct order and recover gracefully from failures.

docker-composehealthchecksreliabilitybest-practices

01The Startup Race Condition

Here's a scenario every Docker Compose user has hit: your web app starts before the database is ready, crashes with "connection refused," and you have to manually restart it. Or worse, it starts, connects to an empty database before migrations run, and serves errors to users. Docker Compose's depends_on without health checks only controls startup order — it ensures container A starts before container B. But "started" doesn't mean "ready." A PostgreSQL container can take 5-10 seconds to initialize on first run, and your app container starts in under a second. Health checks solve this by letting you define what "ready" actually means for each service, and depends_on conditions let you gate startup on those health checks.

02Writing Good Health Checks

A health check is a command that Docker runs periodically inside the container. If it exits with 0, the container is healthy. If it exits with 1, it's unhealthy. Here are health checks for the most common services:
[docker-compose.yml]
1services:
2 postgres:
3 image: postgres:16-alpine
4 healthcheck:
5 test: ["CMD-SHELL", "pg_isready -U ${POSTGRES_USER:-postgres}"]
6 interval: 5s
7 timeout: 5s
8 retries: 5
9 start_period: 10s
10
11 mysql:
12 image: mysql:8.4
13 healthcheck:
14 test: ["CMD", "mysqladmin", "ping", "-h", "localhost"]
15 interval: 5s
16 timeout: 5s
17 retries: 5
18
19 redis:
20 image: redis:7-alpine
21 healthcheck:
22 test: ["CMD", "redis-cli", "ping"]
23 interval: 5s
24 timeout: 3s
25 retries: 5
26
27 app:
28 image: myapp:latest
29 depends_on:
30 postgres:
31 condition: service_healthy
32 redis:
33 condition: service_healthy

Set start_period for services that need initialization time (like databases on first run). During the start period, failed health checks don't count toward the retry limit.

03Depends On Conditions

The depends_on directive supports three conditions: service_started: (default) Wait for the container to start, not for it to be ready. This is what you get with the simple depends_on: [db] syntax. service_healthy: Wait for the container's health check to pass. This is what you want for databases, message queues, and any service your app needs to be fully ready. service_completed_successfully: Wait for the container to run and exit with code 0. Useful for init containers that run migrations or seed data before your app starts.
[docker-compose.yml]
1services:
2 db:
3 image: postgres:16-alpine
4 healthcheck:
5 test: ["CMD-SHELL", "pg_isready"]
6 interval: 5s
7 timeout: 5s
8 retries: 5
9
10 migrate:
11 image: myapp:latest
12 command: ["npm", "run", "migrate"]
13 depends_on:
14 db:
15 condition: service_healthy
16
17 app:
18 image: myapp:latest
19 depends_on:
20 migrate:
21 condition: service_completed_successfully

04Combining with Restart Policies

Health checks work hand-in-hand with restart policies. If a container becomes unhealthy, Docker can automatically restart it: restart: unless-stopped is my recommendation for most services. It restarts the container if it crashes but doesn't restart it if you intentionally stop it. For services with health checks, Docker will restart unhealthy containers according to the restart policy. Combined with depends_on conditions, this gives you a self-healing stack: if the database restarts, dependent services wait for it to become healthy again before reconnecting. This is one of the simplest ways to improve the reliability of your Docker Compose deployments. Every recipe on docker.recipes includes appropriate health checks for database and cache services — browse any of our full-stack recipes to see the pattern in action.

About the Author

Frank Pegasus

DevOps engineer and self-hosting enthusiast with over a decade of experience running containerized workloads in production. Creator of docker.recipes.