HEALTHCHECK Done Right
A good healthcheck tests real readiness, sets sane intervals and a start period, and lets orchestrators route traffic only to healthy containers.
A container that is running is not necessarily working. A HEALTHCHECK teaches Docker (and Compose, Swarm, and load balancers) the difference, so traffic only goes to containers that can actually serve it.
Test something real
Curl a lightweight /healthz endpoint that confirms the app is truly ready — ideally checking its own critical dependencies. Do not just check that a port is open; a process can accept connections while being completely wedged.
Tune the timings
--interval— how often to check (e.g. every 30s).--timeout— how long to wait before calling a check failed.--retries— consecutive failures before marking unhealthy.--start-period— grace window during slow startup where failures don't count. This is the flag people forget, and it's why apps flap on boot.
Use it in Compose
Pair a service healthcheck with depends_on: condition: service_healthy so your app waits for the database to be genuinely ready, not just started.
Example
FROM node:20-alpine
WORKDIR /app
COPY . .
RUN npm ci --omit=dev
# Real readiness probe with a startup grace window
HEALTHCHECK --interval=30s --timeout=3s --start-period=20s --retries=3 \
CMD wget -qO- http://localhost:3000/healthz || exit 1
EXPOSE 3000
CMD ["node", "server.js"]When to use it
- A production API container uses a HEALTHCHECK with a start-period of 30 seconds so Docker does not declare it unhealthy while the JVM warms up.
- Docker Compose uses the health check result to control depends_on so the migration service never runs before the database reports healthy.
- An ops engineer monitors unhealthy containers by parsing docker inspect output in a cron job and paging on-call if any service fails.
More examples
Health check with start period
Sets a 20-second grace period so the container is not marked unhealthy before the app has had time to start.
FROM node:20-alpine
WORKDIR /app
COPY . .
RUN npm ci --omit=dev
HEALTHCHECK \
--interval=30s \
--timeout=5s \
--start-period=20s \
--retries=3 \
CMD wget -qO- http://localhost:3000/healthz || exit 1
CMD ["node", "index.js"]Check health from the CLI
Extracts the current health status and the stdout of the most recent health probe in a single command.
# One-liner: show status and last check output
docker inspect myapp \
--format '{{.State.Health.Status}} — {{(index .State.Health.Log 0).Output}}'Compose depends on service_healthy
Configures Compose to start the api service only after the db's health check passes, not just when the container is created.
services:
db:
image: postgres:16-alpine
healthcheck:
test: ["CMD-SHELL", "pg_isready -U postgres"]
interval: 5s
timeout: 5s
retries: 10
start_period: 10s
api:
build: .
depends_on:
db:
condition: service_healthy
Discussion