Skip to content

Middleware

Health Check

Answer load balancer and uptime probes with 204, or 503 when a check fails.

Health Check answers load balancer and orchestrator probes, such as a Kubernetes liveness check. It replies 204 No Content without running your routes, and 503 Service Unavailable when a check you supply fails.

import "github.com/0mjs/zinc/middleware/healthcheck"
app.Use(healthcheck.New()) // answers GET and HEAD /healthz
Terminal window
curl -i http://localhost:8080/healthz
# HTTP/1.1 204 No Content

Only GET and HEAD requests for exactly /healthz are answered. Other methods and paths continue down the chain as usual.

Register it before logger if you don’t want a log line for every probe.

Setting Default
Path /healthz (healthcheck.DefaultPath)
Check none: always healthy

Give it a Check to report readiness. This one fails while the database is unreachable:

app.Use(healthcheck.New(healthcheck.Config{
Path: "/readyz",
Check: func(c *zinc.Context) error {
return db.PingContext(c.Context()) // db: your *sql.DB
},
}))
Terminal window
curl -i http://localhost:8080/readyz # while the database is down
# HTTP/1.1 503 Service Unavailable
# {"error":{"status":503,"message":"unhealthy"}}
Field Default Meaning
Path /healthz Path to answer. It’s matched exactly: case and a trailing slash both count.
Check none func(*zinc.Context) error. A non-nil error answers 503. Nil means always healthy.

Register it once per probe:

app.Use(
healthcheck.New(), // liveness: the process is up
healthcheck.New(healthcheck.Config{Path: "/readyz", Check: checkDependencies}), // checkDependencies: your check
)

A failing check returns zinc.ServiceUnavailable("unhealthy"), wrapping your error. The client only ever sees unhealthy; your error’s text is not sent. It’s still available to middleware and the error handler with errors.Is and errors.As.

The logger’s error key shows unhealthy, the message of the outer error:

2026/09/28 01:17:01 ERROR REQUEST_ERROR method=GET uri=/readyz status=503 ... error=unhealthy