Compose Health Check — Waiting for Dependencies to Be Ready
What Is a Compose Health Check in Simple Terms?
Docker starts containers quickly — but services inside containers take time to be ready. PostgreSQL takes 3-5 seconds to accept connections after its container starts. Without health checks, your API container starts immediately, tries to connect to a PostgreSQL that is not ready yet, crashes, and goes into a restart loop.
A health check tells Docker how to verify a service is actually ready — not just started.
◈ DIAGRAM
WITHOUT health check: postgres container starts api container starts immediately (depends_on: postgres) api tries DB connection -> connection refused api crashes -> restart loop WITH health check: postgres container starts postgres health check: pg_isready runs every 5s postgres becomes healthy (pg_isready returns 0) THEN api starts api connects successfullyHealth Check Configuration
YAML
services: postgres: image: postgres:15-alpine environment: POSTGRES_PASSWORD: secret healthcheck: test: ["CMD-SHELL", "pg_isready -U postgres"] interval: 5s # run every 5 seconds timeout: 3s # must complete within 3 seconds retries: 5 # mark unhealthy after 5 consecutive failures start_period: 10s # wait 10s before first check (startup grace period) api: depends_on: postgres: condition: service_healthy # wait until postgres is healthyHealth Check Commands for Common Services
YAML
# PostgreSQLhealthcheck: test: ["CMD-SHELL", "pg_isready -U postgres -d mydb"] # MySQLhealthcheck: test: ["CMD", "mysqladmin", "ping", "-h", "localhost"] # Redishealthcheck: test: ["CMD", "redis-cli", "ping"] # HTTP endpoint (requires curl in image)healthcheck: test: ["CMD", "curl", "-f", "http://localhost:8080/health"] # HTTP endpoint (wget — available in Alpine)healthcheck: test: ["CMD", "wget", "--no-verbose", "--tries=1", "--spider", "http://localhost:8080/health"] # RabbitMQhealthcheck: test: ["CMD", "rabbitmq-diagnostics", "ping"] start_period: 30s # RabbitMQ takes longer to start # MongoDBhealthcheck: test: ["CMD", "mongosh", "--eval", "db.adminCommand('ping')"]Container Health States
Bash
# Check health statusdocker compose ps# NAME STATUS# postgres Up 2m (healthy) <- passing health check# api Up 1m (healthy)# redis Up 2m (unhealthy) <- failing health check! # See health check output and failure reasondocker inspect redis-container \ --format '{{json .State.Health}}' | jq# {# "Status": "unhealthy",# "FailingStreak": 3,# "Log": [{# "ExitCode": 1,# "Output": "Could not connect to Redis at 127.0.0.1:6379"# }]# }PLACEMENT PRO TIP**Tip:** Always set `start_period` for services that take time to initialise. Without it, health check failures during the normal startup window count toward the failure threshold and can mark the service as unhealthy before it even finished starting. Use `start_period: 10s` for databases and `start_period: 30s` for services like RabbitMQ or Elasticsearch.
COMMON MISTAKE / WARNING**Common Mistake:** Using `depends_on: postgres` without `condition: service_healthy`. Basic `depends_on` only waits for the container to start — not for PostgreSQL to accept connections. Your API will start and immediately try to connect to a database that needs 3-5 more seconds. Always add the health check condition.