Health probes: /health and /ready
Core ships both. Do not write your own — every app repo that did so ended up
maintaining a near-identical copy, and the reason they did was that the only
probe used to be /ready, which was not safe to point a monitor at.
Both are mounted by core with no authentication: a monitor cannot log in.
/health |
/ready |
|
|---|---|---|
| Question | Is the process serving? | Can it do useful work? |
| Checks | nothing | initialised, plus the database answers |
| Degraded app | 200 | 503 |
| Still starting | 200 | 503 |
| Body | {"status": "ok"} |
{"status": "ready"} / {"status": "not_ready", "reason": …} |
| Point it at | container liveness, uptime monitors | load balancers, readiness gates, deploy checks |
Why two
They answer different questions, and one endpoint cannot answer both.
An orchestrator restarts a container whose liveness probe fails. So liveness must fail only when a restart is the right response. A degraded FastPluggy app — one that booted without its database — is live: the process is serving, and the degraded-mode reprobe may heal it seconds later without help. Report that as "dead" and the orchestrator kills it on every check, turning a recoverable outage into a crash loop.
A load balancer, meanwhile, needs the opposite: stop sending traffic the moment
the app cannot serve a request, and resume when it can. That is /ready.
This is why /health takes no dependency at all — not the database, not the
plugin registry, not even the FastPluggy instance. Anything it touches is
something that can fail and trigger a pointless restart.
What they must never return
Both are unauthenticated, so neither returns plugin names, counts, versions,
settings, or any connection detail. /ready's reason is a fixed word from a
closed set (starting, database) and never the driver's error message —
connection errors routinely carry the host, the database name, and sometimes
credentials.
If you need richer diagnostics, put them behind authentication. An open endpoint is the wrong place to answer "which database am I connected to".
Availability during a failed boot
/health is registered in FastPluggy.__init__, before the database is first
probed, and the degraded-mode middleware exempts it. That is deliberate: a boot
that fails never reaches load_app(), so a probe registered there would return
404 in exactly the situation the orchestrator is asking about — and a 404 is
as much a failure as a 503.
/ready no longer triggers a reload
It used to call trigger_reload(plugins_dir), which unlinks a marker file in
the plugins directory on every request. Under --reload that unlink is a
filesystem event, so polling readiness restarted the app; without --reload
it was a stray unlink on every poll. A probe reports state, it does not change
it. trigger_reload is unchanged and still available to the callers whose job
is actually reloading.
If you were relying on GET /ready as a way to force a reload in development,
call trigger_reload directly or restart the dev server.