Health probes
Octopus serves Kubernetes-style liveness, readiness, and startup probes directly on the gateway listen port — the same port kubelet (or an external load balancer) already targets. Probes are answered before request accounting, so polling them during a drain never inflates the in-flight counter or blocks graceful shutdown.
The three endpoints01
| Path | Probe | Returns 200 when | Behaviour during drain |
/livez | liveness | The process has not fully stopped. | Stays 200 while draining. |
/readyz | readiness | Running and config loaded and not draining and (if required) discovery has synced. | Flips to 503 the instant SIGTERM is received. |
/startupz | startup | The listener has bound. | Gates liveness/readiness while the process boots. |
Each probe returns a small JSON body and Cache-Control: no-store:
curl -i http://localhost:8080/livez
# HTTP/1.1 200 OK
# Content-Type: application/json
# Cache-Control: no-store
#
# {"status":"ok","probe":"live"}A not-yet-ready or draining instance returns 503 with
{"status":"unavailable","probe":"ready"}.
Readiness aliases
/healthz and /health are accepted as back-compat readiness aliases — they
map to the same logic as /readyz.
What each probe actually checks02
The probes are backed by a single lock-free lifecycle state:
Liveness is true unless the process has fully stopped. It deliberately does not depend on readiness, so a draining-but-healthy pod stays live and Kubernetes lets it finish in-flight requests instead of killing it mid-drain.
Readiness requires the accept loop to be running, configuration to be loaded, the instance to not be draining, and — when discovery is required — the initial service-discovery sync to have completed.
Startup becomes true once the listener has bound to its address.
Never point a liveness probe at /readyz. Doing so causes Kubernetes to kill
the pod the moment it starts draining, cutting off in-flight requests. Use
/livez for liveness and /readyz for readiness.
Drain semantics on SIGTERM03
The readiness flip is the foundation of a zero-drop rolling update. On SIGTERM
(or SIGINT):
The instance begins draining —
/readyzreturns503immediately, so Kubernetes endpoint controllers and load balancers stop sending new traffic.The gateway keeps accepting new connections for
gateway.pre_stop_delay, covering the window before kube-proxy / the load balancer notices the readiness change./livezstays200throughout.After the pre-stop delay, the accept loop stops and the gateway drains in-flight requests for up to
gateway.shutdown_timeout.The process marks itself stopped —
/liveznow returns503— and exits.
pre_stop_delay and shutdown_timeout are configured under the
gateway section, not under observability.
Configuration04
Probe paths and the enable switch live under gateway.probes:
gateway:
probes:
enabled: true
liveness_path: /livez
readiness_path: /readyz
startup_path: /startupz
require_discovery_sync: true # readiness waits for first discovery sync
pre_stop_delay: 5s # keep accepting after SIGTERM
shutdown_timeout: 30s # max in-flight drain window| Key | Type | Default | Description |
probes.enabled | boolean | true | Serve the probe endpoints at all. |
probes.liveness_path | string | /livez | Liveness path. |
probes.readiness_path | string | /readyz | Readiness path. |
probes.startup_path | string | /startupz | Startup path. |
probes.require_discovery_sync | boolean | true | Readiness waits for the first service-discovery sync (only when discovery is configured). |
When probes.enabled is false, the probe paths are not intercepted and fall
through to normal request dispatch.
Mapping to Kubernetes05
For the full deployment recipe — probe stanzas, terminationGracePeriodSeconds,
and how pre_stop_delay / shutdown_timeout line up with the drain — see
Kubernetes → Probes & drain.
Related06
Configuration → Gateway —
probes,pre_stop_delay,shutdown_timeout.Concepts → Health checks — upstream health checking (distinct from these process probes).