KhueApps
Home/DevOps/Fix Docker Compose: service_healthy but no healthcheck defined

Fix Docker Compose: service_healthy but no healthcheck defined

Last updated: October 07, 2025

What this error means

You used depends_on with condition: service_healthy, but the dependency service has no healthcheck. Docker Compose can only wait for a service to become "healthy" if that service defines a healthcheck. Without it, Compose raises:

  • depends_on contains condition: service_healthy but no healthcheck defined

Fix it by adding a healthcheck to the dependency, or by using a different condition.

Minimal working example (MWE)

The example below starts an API only after Postgres reports healthy.

dervices:
  db:
    image: postgres:16
    environment:
      POSTGRES_PASSWORD: example
      POSTGRES_USER: postgres
      POSTGRES_DB: app
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U postgres -d app"]
      interval: 5s
      timeout: 3s
      retries: 5
      start_period: 10s

  api:
    image: alpine:3.20
    depends_on:
      db:
        condition: service_healthy
    command: ["sh", "-c", "echo DB healthy; sleep 5"]
  • db declares a healthcheck using pg_isready.
  • api waits until db is healthy before it starts.

Run it:

docker compose up --wait

--wait makes compose block until services are “running/healthy”.

Quickstart

  1. Identify the dependency referenced with service_healthy.
    • Example: web depends_on: db: { condition: service_healthy }
  2. Add a healthcheck to that dependency service.
    • Use a fast, reliable command that proves readiness (not just that the port is open).
  3. Tune healthcheck timings.
    • Use start_period to give the service time to warm up before counting retries.
  4. Validate the file.
    • docker compose config
  5. Start the stack.
    • docker compose up -d
  6. Verify health.
    • docker compose ps

When to use which condition

conditionWhat it waits forNeeds healthcheck
service_startedContainer created and startedNo
service_healthyHealthcheck status is healthyYes
service_completed_successfullyOne-shot container exited 0No

If you cannot define a healthcheck, use service_started or a custom wait script.

Adding useful healthchecks

  • Databases (Postgres):
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U postgres -d app"]
      interval: 5s
      timeout: 3s
      retries: 5
      start_period: 10s
    
  • HTTP apps:
    healthcheck:
      test: ["CMD-SHELL", "wget -qO- http://localhost:8080/health || exit 1"]
      interval: 5s
      timeout: 2s
      retries: 10
      start_period: 15s
    
    Ensure wget or curl exists in the image, or use a minimal script baked into the image.

Common pitfalls

  • Missing healthcheck on the dependency: service_healthy only works if the dependency defines a healthcheck.
  • Wrong service targeted: Verify the service name under depends_on matches the service with healthcheck.
  • Indentation errors: healthcheck must be under the service, not top-level.
  • Flaky healthcheck commands: Avoid commands that rely on external DNS or network; prefer local checks.
  • Start-up grace period: Without start_period, the first few checks may fail before the service is actually ready.
  • Wrong binary in image: If you use curl/wget/pg_isready, ensure it exists in the image.
  • Swarm mode: docker stack deploy ignores depends_on; use alternative orchestration logic for Swarm.

Performance notes

  • Keep interval reasonable (e.g., 5–30s). Very frequent healthchecks across many services add CPU overhead.
  • Use simple commands. Each healthcheck spawns a process; heavier checks slow hosts.
  • Increase start_period for services that need warm-up (databases, JVMs) to reduce retries and log noise.
  • In development, consider longer intervals or fewer retries to speed up compose up.

Alternative fixes

  • Switch to service_started:

    depends_on:
      db:
        condition: service_started
    

    Use this when the application can tolerate connecting retries on its own.

  • App-level retry logic: Implement connection retries in the app (e.g., exponential backoff) so it can handle restarts of dependencies.

  • External wait tools: As a last resort, a tiny entrypoint script can poll the dependency (e.g., nc/wait-for) before starting the app.

Debugging checklist

  • docker compose config to validate the YAML.
  • docker compose ps to see health state (starting, healthy, unhealthy).
  • docker logs <service> to inspect healthcheck failures.
  • docker inspect --format '{{json .State.Health}}' <container> for detailed status and log output.

FAQ

  • Do I need a healthcheck on every service? No. Only services referenced with condition: service_healthy require it.

  • Why does service_started not wait for readiness? It only waits for the container process to start, not for the app to be ready.

  • Is depends_on honored in Swarm? Not for ordering; Swarm ignores depends_on. Use init jobs, orchestrator features, or app-level retries instead.

  • Can healthchecks restart unhealthy containers? Healthchecks mark containers unhealthy. Combine with restart policies if you want automatic restarts.

  • What if the error persists? Confirm the dependency actually defines healthcheck, names match, and your Compose CLI supports depends_on conditions (use the docker compose V2 plugin).

Series: Docker

DevOps