Health Checks
Liveness and readiness handlers for Ply services.
Health Checks
Ply provides two handlers for orchestrators and load balancers.
app.Get("/healthz", middleware.Health())
app.Get("/readyz", middleware.Readiness(
func() error { return db.Ping() },
))Both handlers set Cache-Control: no-store.
Liveness
Health always returns 200:
{
"status": "ok"
}Use it to show that the process is running. Keep it free of database and network checks. A failed dependency should not restart an otherwise healthy process.
Readiness
Readiness runs the checks in order and returns 200 when all succeed:
{
"status": "ok"
}If any check returns an error, the response is 503:
{
"status": "unavailable"
}The error value is not included in the response. Log it in the check or in middleware if you need the details.
Readiness with no checks always returns 200.
Async checks
Checks run synchronously in the request. A slow database ping delays the readiness response. Make checks cheap, use a short internal timeout, and cache the dependency state when the check is expensive.
app.Get("/readyz", middleware.Readiness(func() error {
ctx, cancel := context.WithTimeout(context.Background(), 500*time.Millisecond)
defer cancel()
return db.PingContext(ctx)
}))Shutdown
Stop reporting ready when shutdown starts. Set an atomic flag in your signal handler and include it in the readiness route:
var shuttingDown atomic.Bool
app.Get("/readyz", middleware.Readiness(func() error {
if shuttingDown.Load() {
return errors.New("shutting down")
}
return nil
}))This gives the load balancer time to remove the instance before the listener closes. See Graceful Shutdown.
Access control
Health routes are often public. Return only a status value, not build details,
configuration, or dependency errors. Restrict /metrics separately.

Ply