Healthchecks
Docker healthchecks: HEALTHCHECK in Dockerfile, compose health, and the patterns for reliable orchestrator decisions.
Docker — healthchecks
EXAMPLE
# ===== Why =====
# Containers can be RUNNING but UNHEALTHY (deadlocked, OOM-ed thread, db disconnected).
# Healthchecks let Docker / orchestrators detect this and replace the container.
# ===== HEALTHCHECK in Dockerfile =====
FROM node:20-alpine
WORKDIR /app
COPY . .
RUN npm ci --omit=dev
HEALTHCHECK --interval=30s --timeout=10s --retries=3 --start-period=20s \
CMD wget -qO- http://localhost:3000/healthz || exit 1
CMD ["node", "server.js"]
# Options:
# --interval between checks (default 30s)
# --timeout max check duration (default 30s)
# --retries failures before unhealthy (default 3)
# --start-period grace period before failures count (default 0s)
# ===== Check states =====
# starting -> within start-period; failures don't count
# healthy -> last check passed
# unhealthy -> retries exhausted
docker ps --format 'table {{.Names}}\t{{.Status}}'
# NAME STATUS
# api Up 5 minutes (healthy)
# ===== docker compose =====
services:
api:
build: .
healthcheck:
test: ["CMD", "curl", "-fsS", "http://localhost:3000/healthz"]
interval: 30s
timeout: 10s
retries: 3
start_period: 20s
app:
image: myapp:1
depends_on:
db:
condition: service_healthy # wait for db healthcheck
db:
image: postgres:16
healthcheck:
test: ["CMD-SHELL", "pg_isready -U postgres"]
interval: 10s
timeout: 5s
# ===== Application healthcheck shape =====
# A good /healthz:
# - Returns 200 if the app is RUNNING (not crashed)
# - Returns 500 if a CRITICAL dependency is down
# - Does NOT do heavy work (cheap to call)
# Distinguish:
# /healthz liveness (is the process alive?)
# /readyz readiness (is it ready to receive traffic?)
# Kubernetes uses both; Docker only has one HEALTHCHECK.
# ===== Common patterns =====
# Express example:
app.get('/healthz', (req, res) => res.json({ ok: true }));
app.get('/readyz', async (req, res) => {
try {
await db.ping();
await cache.ping();
res.json({ ok: true });
} catch (e) {
res.status(500).json({ ok: false, error: e.message });
}
});
# ===== Orchestrator behavior =====
# Docker: unhealthy containers stay running but appear (unhealthy) in ps
# Compose: depends_on + service_healthy waits before starting dependents
# Swarm: replaces unhealthy tasks
# Kubernetes: ignores HEALTHCHECK in Dockerfile; uses pod liveness/readiness probes
# ===== Patterns =====
# - HEALTHCHECK in Dockerfile for portable images
# - Cheap, fast endpoints; no DB calls in liveness
# - depends_on: condition: service_healthy in compose
# - Distinguish liveness vs readiness
# ===== Pitfalls =====
# - Healthcheck calling 'localhost' before app listens -> false failures
# - Healthcheck that hits external services -> coupled failure modes
# - Long timeouts hiding hangs
# - No start_period on slow-booting apps -> instant unhealthy
Why it matters
HEALTHCHECK in Dockerfile + healthcheck in compose make container health observable. Keep them cheap, distinguish liveness from readiness, use depends_on: service_healthy to sequence startup. Kubernetes uses its own probes; Docker / compose lean on this.
Tip: Tweak the snippet with Try it Yourself », then sit the quiz at the bottom of the page.
Example
Example
HEALTHCHECK --interval=30s CMD wget -qO- http://localhost:3000/health || exit 1Try it Yourself »
Discussion
Loading…