Health checks & readiness
What /health and /ready actually verify — and what to alert on.
Ductor exposes liveness and readiness endpoints on the HTTP listener, plus a
health CLI subcommand. The distinction between liveness and readiness matters:
liveness answers "is the process alive?", readiness answers "can it serve traffic
right now?".
The endpoints
All are served on the HTTP listener (server.http_addr, default :8080):
| Path | Kind | Returns |
|---|---|---|
/health | Liveness | 200 {"status":"ok"} as long as the process is running |
/ready | Readiness | 200 when internal readiness is set and backing dependencies are reachable; 503 otherwise |
Registered aliases
/healthz and /livez are registered aliases for /health; /readyz is a
registered alias for /ready. Kubernetes convention accepts either spelling —
use whichever your probe tooling expects. This page uses the canonical
/health and /ready throughout.
Liveness — process is up
The liveness handler returns 200 unconditionally while the process runs. It does
not check dependencies — a liveness failure means the process itself is wedged
and should be restarted. Use it for the container/Kubernetes liveness probe so
a transient database blip doesn't cause a restart loop.
Readiness — safe to route traffic
The readiness handler returns 200 only when both are true:
- Internal readiness is set — the app finished booting and wiring.
- Dependencies are reachable — a live check against Postgres and Redis, bounded by a short timeout so a slow backend doesn't hang the probe.
Because readiness re-checks dependencies on every call, it flips to 503 if
Postgres or Redis becomes unreachable after boot — not just at startup. Use it
for the readiness probe and load-balancer health so an unhealthy pod is pulled
from rotation without being killed.
The CLI check
ductor health performs an HTTP GET /health against a running server and prints
OK or the error. It's handy for scripts and the container healthcheck:
ductor health --http-addr :8080The container image's built-in healthcheck does the equivalent
(wget --spider http://localhost:8080/health).
Probe configuration
Docker Compose
The image ships a healthcheck against /health (interval 10s, timeout 3s, 3
retries). No extra config needed.
Kubernetes (Helm)
The chart wires all three probes to the right endpoints:
| Probe | Path | Why |
|---|---|---|
startupProbe | /ready | Hold traffic until boot + dependencies are good |
readinessProbe | /ready | Pull the pod from Service endpoints when not ready |
livenessProbe | /health | Restart only when the process is wedged |
Don't point liveness at /ready
If you use a readiness endpoint for the liveness probe, a brief Postgres outage
makes /ready fail, Kubernetes restarts every pod, and you turn a dependency
blip into an outage. Keep liveness on /health and readiness on /ready.
What to alert on
- Readiness failing across replicas — dependencies (Postgres/Redis) unreachable, or a bad rollout. This is your primary "is Ductor serving?" alert.
- Coordinator health — stuck runs and dead-lettered wakeups
(
workflow_dag.coordinator.stuck_run_threshold,workflow_dag.coordinator.wakeup_dead_letter_threshold) surface in the DAG metrics. - Pending migrations —
servelogs a warning at startup;ductor schema statusexits non-zero. Gate deploys on it.
See Observability for wiring these signals into metrics and alerts.