Health & Readiness
Two probes every deployment needs, at the paths Kubernetes uses by default.
core.RegisterHealthRoutes(e, options)That registers GET /healthz and GET /readyz. They go on the server rather than on an authenticated group — a probe that needs a token reports the gateway's health instead of this process's.
The two probes answer different questions
/healthz — liveness. Is this process wedged?
It touches no dependency, on purpose. The only correct answer to "should this process be restarted?" is one that cannot fail for any other reason. A liveness probe that pings the database restarts every instance of the service the moment the database hiccups, turning one outage into two.
{ "status": "up" }/readyz — readiness. Can this instance serve traffic?
It probes every dependency at once and reports each one.
{
"status": "degraded",
"service": "orders",
"took_ms": 12,
"checks": {
"database": { "status": "up", "critical": true, "took_ms": 4 },
"cache": { "status": "down", "error": "dial tcp: connection refused", "took_ms": 11 }
}
}| Status | HTTP | Meaning |
|---|---|---|
up | 200 | every critical dependency answered |
degraded | 200 | a non-critical dependency is down — still serving, just not everything |
down | 503 | a critical dependency is down |
The 503 is what takes an instance out of the load balancer without restarting it, which is what you want while a database is recovering.
What gets probed
Only what the deployment actually has. A service with no redis gets no cache check, rather than a cache check that always fails.
| Dependency | Critical |
|---|---|
DB and every DBS connection | yes |
MongoDB and every MongoDBS | yes |
Cache and every Caches entry | no |
MQ | no |
"Critical" is the common case, not a rule. Ask: can this instance still do its job without it? Replace the list when the answer differs for your service.
Adding your own checks
Anything the framework cannot see — a partner API, a license server, a mounted volume:
core.RegisterHealthRoutes(e, options, core.HealthOptions{
Checks: []core.HealthCheck{{
Name: "payments-api",
Critical: true,
Check: func(ctx context.Context) error {
_, err := payments.Ping(ctx)
return err
},
}},
})Use Only instead of Checks to replace the framework's list entirely, for a service that wants to name exactly what it probes.
Respect the context
Check receives a context carrying the probe's deadline. A check that ignores it is a check that can hang the probe.
Options
core.HealthOptions{
Timeout: 3 * time.Second, // bounds the whole probe
Details: nil, // include error text in the body
Checks: nil, // extra checks, alongside the framework's
Only: nil, // replace the framework's checks entirely
}Timeout bounds the whole probe (default 3s). Checks run in parallel, so the probe takes as long as the slowest one rather than the sum — which is what lets the timeout be tight enough to be useful. A check that has not answered by then is reported as down.
Details decides whether each dependency's error text appears in the body. Left nil it follows the environment: on outside production, off in it. An error string can name a host, a user or a bucket, and a probe endpoint is often the one route nobody remembers to put behind the gateway.
Checking health from your own code
For a startup gate, a CLI, or a route of your own shape:
report := core.CheckHealth(ctx, options, core.HealthOptions{Timeout: time.Second})
if report.Status == core.HealthDown {
return fmt.Errorf("dependencies not ready: %+v", report.Checks)
}Kubernetes
livenessProbe:
httpGet: { path: /healthz, port: 8080 }
periodSeconds: 10
readinessProbe:
httpGet: { path: /readyz, port: 8080 }
periodSeconds: 5Point liveness at /healthz and readiness at /readyz — not both at the same one. Pointing liveness at /readyz is the mistake this page exists to prevent: it restarts healthy processes because something they depend on is having a bad minute.
A probe that panics does not take the process down
Each check runs guarded. A dependency whose client panics is reported as down with "panic in health check" — the probe is the last thing that should crash the service.