This error occurs when Docker Compose waits for a service to become healthy but the healthcheck never succeeds within the allowed time. The dependency service either lacks a proper healthcheck, the healthcheck command is failing, or the timeout and retry settings are insufficient.
The "dependency condition 'service_healthy' was never satisfied" error indicates that Docker Compose was waiting for a dependent service to pass its healthcheck, but the healthcheck never succeeded. Docker Compose eventually gives up and reports this failure. When you use `depends_on` with `condition: service_healthy`, Docker Compose monitors the dependency container's health status. The container runs a healthcheck command periodically, and only when it returns exit code 0 does Docker consider the service "healthy." If the healthcheck keeps failing beyond the configured retries, the container is marked "unhealthy" and Compose cannot proceed with starting dependent services. This error commonly occurs with databases, message queues, and other services that need initialization time before they're ready to accept connections. The healthcheck might be querying the service before it has finished starting up, or the healthcheck command itself might be misconfigured.
First, diagnose why the healthcheck is failing:
# Check container health status
docker ps
# Get detailed health information
docker inspect --format='{{json .State.Health}}' <container_name> | jq
# View recent healthcheck results
docker inspect --format='{{range .State.Health.Log}}{{.Output}}{{end}}' <container_name>
# Check container logs for startup issues
docker logs <container_name>Look for:
- Exit codes from healthcheck attempts (0 = healthy, non-zero = unhealthy)
- Error messages in the healthcheck output
- Container startup errors that might prevent the service from working
Run the healthcheck command inside the container to see if it works:
# Execute the healthcheck command interactively
docker exec -it <container_name> sh -c "your_healthcheck_command"
# Examples for common services:
# PostgreSQL
docker exec -it db sh -c "pg_isready -U postgres -d postgres"
# MySQL
docker exec -it db sh -c "mysqladmin ping -h localhost -u root -p"
# Redis
docker exec -it redis redis-cli ping
# Generic HTTP service
docker exec -it web curl -f http://localhost:8080/healthIf the command fails, you need to either:
- Fix the service configuration so it starts properly
- Update the healthcheck command to match the service's actual health endpoint
If the service needs more time to start, adjust the healthcheck timing:
services:
db:
image: postgres:16
healthcheck:
test: ["CMD-SHELL", "pg_isready -U postgres -d postgres"]
interval: 10s # How often to run the check
timeout: 5s # How long to wait for each check
retries: 10 # Number of failures before unhealthy
start_period: 60s # Grace period before failures countKey parameters:
- start_period: Time to wait before counting failures. Set this longer than your service's typical startup time
- interval: Time between checks. Don't set too low or you'll spam the service
- retries: Number of consecutive failures before marking unhealthy
- timeout: Maximum time for each check to complete
For slow-starting services (databases, Java apps), use:
healthcheck:
start_period: 120s
interval: 30s
timeout: 10s
retries: 5Many minimal container images don't include common tools. If your healthcheck uses curl, wget, or other utilities, they must be available:
# For Alpine-based images
FROM postgres:16-alpine
RUN apk add --no-cache curl
# For Debian-based images
FROM postgres:16
RUN apt-get update && apt-get install -y curl && rm -rf /var/lib/apt/lists/*Alternatively, use built-in tools:
services:
db:
image: postgres:16
healthcheck:
# pg_isready is included in postgres image
test: ["CMD-SHELL", "pg_isready -U postgres"]
redis:
image: redis:7
healthcheck:
# redis-cli is included in redis image
test: ["CMD", "redis-cli", "ping"]
mysql:
image: mysql:8
healthcheck:
# mysqladmin is included in mysql image
test: ["CMD", "mysqladmin", "ping", "-h", "localhost"]Ensure your healthcheck uses the correct syntax:
services:
web:
image: nginx
healthcheck:
# Option 1: CMD-SHELL (runs via shell, allows pipes and redirects)
test: ["CMD-SHELL", "curl -f http://localhost/ || exit 1"]
# Option 2: CMD (direct execution, more efficient)
test: ["CMD", "curl", "-f", "http://localhost/"]
# Option 3: String format (uses shell automatically)
test: curl -f http://localhost/ || exit 1Common mistakes:
# WRONG - Missing exit on failure
test: ["CMD-SHELL", "curl http://localhost/"]
# WRONG - Wrong array format
test: ["curl", "-f", "http://localhost/"] # Missing CMD or CMD-SHELL
# CORRECT - Explicit failure handling
test: ["CMD-SHELL", "curl -f http://localhost/ || exit 1"]If you only need the container to be running (not necessarily ready), use service_started instead:
services:
app:
build: .
depends_on:
db:
condition: service_started # Just wait for container to start
# Handle connection retries in your application codeThis is appropriate when:
- Your application already handles connection retries
- You want faster startup in development
- The dependency doesn't have a meaningful healthcheck
For production, prefer service_healthy with proper healthchecks to ensure services are truly ready.
Understanding healthcheck timing: The first healthcheck runs after interval seconds (default 30s), not immediately. The start_period doesn't delay the first check - it only determines when failures start counting against the retry limit. For a service that takes 45 seconds to start with default settings (30s interval, 30s start_period), the first real failure would occur at the 60-second mark.
Debugging with docker events: Watch real-time health status changes:
docker events --filter 'event=health_status'Compose profiles for faster development: Skip healthchecks in development:
services:
db:
profiles: ["prod"]
healthcheck:
test: ["CMD-SHELL", "pg_isready"]
db-dev:
profiles: ["dev"]
healthcheck:
disable: trueThe restart: true option: Combined with healthcheck, this restarts dependent services when the dependency restarts:
depends_on:
db:
condition: service_healthy
restart: trueResource-constrained environments: In CI/CD or resource-limited systems, services may start slower. Use environment-specific overrides:
# docker-compose.ci.yml
services:
db:
healthcheck:
start_period: 180s
retries: 15Podman users: Note that podman-compose may have limited support for the service_healthy condition. Check your podman-compose version and consider using healthcheck polling scripts as a workaround.
image operating system "linux" cannot be used on this platform
How to fix 'image operating system linux cannot be used on this platform' in Docker
manifest unknown: manifest unknown
How to fix 'manifest unknown' in Docker
cannot open '/etc/passwd': Permission denied
How to fix 'cannot open: Permission denied' in Docker
Error response from daemon: failed to create the ipvlan port
How to fix 'failed to create the ipvlan port' in Docker
toomanyrequests: Rate exceeded for anonymous users
How to fix 'Rate exceeded for anonymous users' in Docker Hub