What is a Docker HEALTHCHECK, and how does it affect container status?

6 minintermediatehealthcheckcontainer-lifecycle

Quick Answer

A HEALTHCHECK instruction defines a command Docker periodically runs inside the container to check whether the application is actually working, not just whether its process is still alive. Based on the command's exit code, Docker tracks the container's health as starting, healthy, or unhealthy. This is visible in docker ps and usable by other tooling, like Compose's depends_on: condition: service_healthy, to decide a container's real readiness instead of just whether its process is technically running.

Detailed Answer

Why "the process is running" isn't the same as "the application works"

A container can be Running — its main process hasn't crashed — while the application inside is completely non-functional. It could be deadlocked, unable to reach a required database, or stuck in an infinite loop that never actually serves a request. Docker's basic process-based status can't tell "truly working" apart from "technically still running but broken" without an explicit healthcheck telling it how to verify functionality.

Defining a HEALTHCHECK

HEALTHCHECK --interval=30s --timeout=5s --retries=3 --start-period=10s \
  CMD curl -f http://localhost:3000/health || exit 1
  • --interval — how often to run the check (every 30 seconds here).
  • --timeout — how long to wait for the check command before counting that attempt as a failure.
  • --retries — how many consecutive failures are needed before marking the container unhealthy. A single blip doesn't immediately flip the status.
  • --start-period — a grace period after the container starts during which failures don't count against the retry threshold. Similar in spirit to Kubernetes's startup probe, useful for applications with a legitimately slow startup that shouldn't be judged by the same strict timing as steady-state checks.
  • CMD ... — the actual command run to check health. Exit code 0 means healthy; any non-zero code means unhealthy for that attempt.

Observing health status

docker ps
# STATUS: Up 2 minutes (healthy)
# or:
# STATUS: Up 5 minutes (unhealthy)
docker inspect --format='{{.State.Health.Status}}' my-container
docker inspect --format='{{json .State.Health}}' my-container | jq

The container's health status is visible directly in docker ps output. docker inspect shows the full history of recent check results, useful for diagnosing exactly when and why a container went unhealthy.

How this differs from and relates to Kubernetes's probes

Docker's HEALTHCHECK is a single, combined mechanism, roughly similar to Kubernetes's liveness probe. But plain Docker has no automatic action tied to an unhealthy status by default — it's purely informational, unless something else actively reacts to it, like a restart policy combined with a health-aware supervisor, an orchestrator, or Compose's condition: service_healthy dependency. This is a meaningful difference from Kubernetes, where a failing liveness probe automatically triggers a container restart. Plain Docker's HEALTHCHECK alone doesn't restart anything. You generally need Docker Swarm, Kubernetes, or custom tooling layered on top to actually act on an unhealthy status.

A common practical use: gating startup order in Compose

services:
  db:
    image: postgres:16
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U postgres"]
      interval: 5s
      retries: 5
  app:
    image: myapp:1.0
    depends_on:
      db:
        condition: service_healthy

This ensures the app service doesn't start until db's healthcheck actually reports healthy — a stronger guarantee than Compose's plain depends_on, which by default only waits for the dependency container to start, not to be ready. Add a HEALTHCHECK to any image where "the process is running" genuinely isn't enough evidence that the application works. This matters most for services with real startup dependencies — a database connection, a cache warm-up — where a naive restart policy or dependency ordering based purely on process-start would be unreliable.