Docker Compose — Compose Health Checks

1 minute read

Health checks let Docker test whether a service is truly ready to accept connections, not just running.

Defining a Health Check

services:
  db:
    image: postgres:16
    environment:
      POSTGRES_PASSWORD: secret
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U postgres"]
      interval: 5s
      timeout: 3s
      retries: 5
      start_period: 10s

Health Check Options

Option Default Description
test (none) Command to run
interval 30s Time between checks
timeout 30s Max time for check to respond
retries 3 Consecutive failures before unhealthy
start_period 0s Grace period on startup

Test Formats

# Shell form (uses /bin/sh)
test: ["CMD-SHELL", "curl -f http://localhost:3000/health"]

# Exec form (no shell)
test: ["CMD", "pg_isready", "-U", "postgres"]

# Disable health check
test: ["NONE"]

Health Check States

Container starts → starting → healthy (checks pass)
                            → unhealthy (retries exhausted)

Timeline:
0s ────── start_period ──────▶ First check
                                  │
                               Pass? → healthy ✓
                               Fail? → retry (up to N times)
                                          │
                                       All fail? → unhealthy ✗

Common Health Checks

PostgreSQL

healthcheck:
  test: ["CMD-SHELL", "pg_isready -U postgres"]
  interval: 5s
  timeout: 3s
  retries: 5

MySQL

healthcheck:
  test: ["CMD", "mysqladmin", "ping", "-h", "localhost"]
  interval: 5s
  timeout: 3s
  retries: 5

Redis

healthcheck:
  test: ["CMD", "redis-cli", "ping"]
  interval: 5s
  timeout: 3s
  retries: 5

HTTP Service

healthcheck:
  test: ["CMD-SHELL", "curl -f http://localhost:3000/health || exit 1"]
  interval: 10s
  timeout: 5s
  retries: 3

Nginx

healthcheck:
  test: ["CMD-SHELL", "curl -f http://localhost/ || exit 1"]
  interval: 10s
  timeout: 3s
  retries: 3

Monitoring Health

docker compose ps
NAME        STATUS              PORTS
myapp-db-1  Up 2 minutes (healthy)   5432/tcp
myapp-api-1 Up 1 minute (healthy)    0.0.0.0:3000->3000/tcp
docker inspect --format='{{.State.Health.Status}}' myapp-db-1
healthy

Summary

Component Purpose
test The check command
interval How often to check
timeout Max wait per check
retries Failures before unhealthy
start_period Startup grace period

Quick Reference

Item Detail    
healthcheck: Define in service block    
CMD-SHELL Use shell features (pipes,   )
CMD Exec form, no shell    
States starting → healthy / unhealthy    
Monitor docker compose ps shows health    
start_period Grace period before first check