Skip to content

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.