HEALTHCHECK reference

Last reviewed on 2026-05-02

How the HEALTHCHECK instruction works, how to tune its options, and the pitfalls teams hit when running it in production.

The HEALTHCHECK instruction tells Docker how to test that a container is still working. The runtime calls the configured command at a chosen interval and uses its exit code to set the container's health status. That status is exposed in docker ps, docker inspect, and to higher-level orchestrators that consult it.

Syntax

HEALTHCHECK [OPTIONS] CMD command
HEALTHCHECK NONE

Two forms exist. The first registers a probe; the second explicitly disables a healthcheck inherited from a base image. The CMD here is unrelated to the CMD instruction — it is part of the healthcheck syntax.

Options

OptionDefaultWhat it controls
--interval30sHow often the probe runs after the container is healthy.
--timeout30sHow long a single probe is allowed to take before it counts as a failure.
--start-period0sGrace window during startup. Failures inside this window do not count towards --retries.
--start-interval5sProbe interval during the start period. Lets startup health be detected quickly.
--retries3Consecutive failed probes required before the container is marked unhealthy.

Exit-code semantics

The container starts in the starting state. After the first successful probe (or after enough probes have failed past the retry threshold), it transitions to healthy or unhealthy.

Worked example: HTTP service

FROM nginx:1.27-alpine

HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \
  CMD wget --quiet --tries=1 --spider http://127.0.0.1/ || exit 1

This probe asks the in-container nginx to serve its index. wget --spider performs a HEAD-like request without writing the response to disk; redirecting to exit 1 on failure keeps the script POSIX-clean. --tries=1 is important — without it, wget would retry inside the probe and exhaust the timeout before Docker sees a failure.

Worked example: a service with a dedicated /healthz

FROM gcr.io/distroless/static:nonroot
COPY --from=builder /out/server /server
COPY --from=builder /usr/bin/healthcheck /healthcheck

HEALTHCHECK --interval=10s --timeout=2s --start-period=15s --retries=3 \
  CMD ["/healthcheck", "--addr=127.0.0.1:8080", "--path=/healthz"]

USER nonroot
EXPOSE 8080
ENTRYPOINT ["/server"]

A distroless image has no shell, so the probe must be a single executable invoked with the exec form (the JSON-array form). Many teams ship a tiny healthcheck binary alongside the application for exactly this reason. The binary should set a short timeout, exit non-zero on any error, and never log to stdout.

Tuning interval, timeout, and start period

The defaults are usable but rarely optimal. A useful set of decision criteria:

Common pitfalls

Healthcheck and orchestrators

Most orchestrators have their own probe model. Kubernetes uses liveness, readiness, and startup probes that are configured at the Pod level and override Dockerfile HEALTHCHECK. ECS, Nomad, and Docker Swarm honour HEALTHCHECK directly. As a rule of thumb: include a HEALTHCHECK for portability and for local docker compose use, but assume the orchestrator's own probes are the source of truth in production.

Related