Octopus
1.x
Docs/Octopus/Health probes
Open

Reading3 min
Updated31 Jul 2026
Sourcev1/observability/health-probes.mdx

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

PathProbeReturns 200 whenBehaviour during drain
/livezlivenessThe process has not fully stopped.Stays 200 while draining.
/readyzreadinessRunning and config loaded and not draining and (if required) discovery has synced.Flips to 503 the instant SIGTERM is received.
/startupzstartupThe 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.

Warning

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):

  1. The instance begins draining/readyz returns 503 immediately, so Kubernetes endpoint controllers and load balancers stop sending new traffic.

  2. The gateway keeps accepting new connections for gateway.pre_stop_delay, covering the window before kube-proxy / the load balancer notices the readiness change. /livez stays 200 throughout.

  3. After the pre-stop delay, the accept loop stops and the gateway drains in-flight requests for up to gateway.shutdown_timeout.

  4. The process marks itself stopped — /livez now returns 503 — 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
KeyTypeDefaultDescription
probes.enabledbooleantrueServe the probe endpoints at all.
probes.liveness_pathstring/livezLiveness path.
probes.readiness_pathstring/readyzReadiness path.
probes.startup_pathstring/startupzStartup path.
probes.require_discovery_syncbooleantrueReadiness 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.