“Connection refused” errors? The app starts before the database is ready. Here’s the fix.

What doesn’t work

This only waits for the container to start, not for it to be ready:

services:
  app:
    depends_on:
      - db  # Container starts, but database isn't ready yet

What actually works

Add health checks:

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

  app:
    image: myapp
    depends_on:
      db:
        condition: service_healthy  # Now it actually waits for the database

Common health checks

PostgreSQL:

healthcheck:
  test: ["CMD-SHELL", "pg_isready -U ${POSTGRES_USER:-postgres}"]

MySQL:

healthcheck:
  test: ["CMD", "mysqladmin", "ping", "-h", "localhost"]

Redis:

healthcheck:
  test: ["CMD", "redis-cli", "ping"]

HTTP Service:

healthcheck:
  test: ["CMD", "curl", "-f", "http://localhost:8080/health"]
  # Or wget if curl isn't available
  test: ["CMD-SHELL", "wget --quiet --tries=1 --spider http://localhost:8080/health || exit 1"]

What the options mean

  • interval: How often to run the check
  • timeout: How long to wait for a response
  • retries: Failures before marking unhealthy
  • start_period: Grace period for slow services

Multiple dependencies

services:
  app:
    depends_on:
      db:
        condition: service_healthy
      redis:
        condition: service_started  # Mix different conditions
      migration:
        condition: service_completed_successfully  # Waits for migrations to finish

Development tip

Add restart logic for local dev:

services:
  app:
    depends_on:
      db:
        condition: service_healthy
        restart: true  # Restarts app when db restarts

The app will reconnect when the database restarts.

Debugging

# Check health status
docker compose ps

# See health check output
docker inspect --format='{{json .State.Health}}' <container_name> | jq