Probes & drain
Octopus serves Kubernetes-style health probe endpoints directly on the gateway listen port (the
port kubelet targets) and drains gracefully on SIGTERM. The probes are handled before request
accounting, so polling them during a drain never inflates the in-flight counter or blocks shutdown.
Probe endpoints01
All three endpoints are served on the listen port and return 200 OK (with a small JSON body) when
healthy, or 503 Service Unavailable otherwise.
| Endpoint | Probe | 200 when | Notes |
/livez | liveness | The process has not fully stopped. | Stays 200 while draining — a draining-but-healthy pod must not be killed mid-drain. |
/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. |
/healthz and /health are accepted as back-compat aliases for readiness. The paths are
configurable under gateway.probes (liveness_path, readiness_path, startup_path), and the
endpoints can be turned off with gateway.probes.enabled: false.
Liveness must never depend on readiness. Pointing the liveness probe at /readyz would cause
Kubernetes to kill the pod the moment it starts draining, cutting off in-flight requests. Use
/livez for liveness and /readyz for readiness.
Readiness gating on discovery sync02
When gateway.probes.require_discovery_sync is true (the default) and service discovery is
configured, /readyz stays 503 until the discovery watcher completes its first sync. This keeps a
freshly started pod out of the Service's EndpointSlice until it actually knows where its upstreams
are. When discovery is not configured, this gate is treated as already satisfied, so readiness only
depends on the server running with config loaded.
gateway:
probes:
enabled: true
liveness_path: /livez
readiness_path: /readyz
startup_path: /startupz
require_discovery_sync: trueMapping to Kubernetes probes03
Point all three probes at the listen port. The chart wires this automatically; the equivalent Deployment spec is:
ports:
- name: http
containerPort: 8080
startupProbe:
httpGet:
path: /startupz
port: http
periodSeconds: 2
failureThreshold: 30 # ~60s startup budget
livenessProbe:
httpGet:
path: /livez
port: http
periodSeconds: 10
failureThreshold: 3
readinessProbe:
httpGet:
path: /readyz
port: http
periodSeconds: 5
failureThreshold: 2Graceful drain on SIGTERM04
On SIGTERM, the gateway runs an ordered drain so that traffic stops flowing before the socket
closes:
Readiness flips to
503immediately.begin_draining()is called as soon as the signal arrives, so the very next/readyzpoll fails and Kubernetes removes the pod from the Service EndpointSlice. Liveness stays200.Keep accepting for
gateway.pre_stop_delay(default5s). The server continues to accept during this window so that connections opened before kube-proxy / the EndpointSlice controller observed the pod going NotReady are still served. This closes the race where a client is routed to a pod that has already stopped listening.Stop accepting and drain in-flight up to
gateway.shutdown_timeout. After the delay, the accept loop exits and the server waits for in-flight requests to finish, bounded by the timeout. Then the process marks itself stopped (liveness503) and exits.
gateway:
pre_stop_delay: 5s # keep accepting this long after SIGTERM (0 disables)
shutdown_timeout: 30s # max wait for in-flight requests after we stop acceptingSet pre_stop_delay: 0 if you instead use a Kubernetes preStop hook to own the delay (the chart's
preStopHook option does this) — otherwise the delay is counted twice.
terminationGracePeriodSeconds must be ≥ pre_stop_delay + shutdown_timeout, or the
kubelet SIGKILLs the pod before it finishes draining. The chart defaults to
terminationGracePeriodSeconds: 45 for a 5s delay plus 30s timeout (with headroom).
The chart's Service also sets publishNotReadyAddresses: false, so NotReady (draining) pods are
removed from the EndpointSlice — which is what makes pre_stop_delay actually drain traffic before
the socket closes.
See Discovery for how the readiness discovery gate ties into
EndpointSlice-based service discovery, and the configuration section for the
full gateway block.