Health checks
Designing probes that catch real failures without flapping. HTTP and TCP supported today.
A probe tells Rune whether an instance is doing its job. Failed probes mean the reconciler kills and replaces the instance. Get this right.
Probe types
| Type | What it does |
|---|---|
http | GETs a path. Healthy if 2xx/3xx within timeout. |
tcp | Opens a TCP connection. Healthy if the connect succeeds. |
Process-level health (PID alive) is implicit — that's what the runner watches. Probes are for application-level health.
HTTP probe
health:
liveness:
type: http
path: /healthz
port: 8080
scheme: http # or https
headers:
X-Health-Check: rune
initialDelaySeconds: 5
intervalSeconds: 10
timeoutSeconds: 2
failureThreshold: 3
successThreshold: 1 # must be 1 on liveness; see below
Tunables:
| Field | Default | Notes |
|---|---|---|
initialDelaySeconds | 0 | Wait this long after container start before first probe. |
intervalSeconds | 10 | Time between probes. |
timeoutSeconds | 5 | Per-probe timeout, for http, tcp and exec probes. |
failureThreshold | 3 | Consecutive failures before acting: liveness restarts the instance, readiness withdraws it from traffic. |
successThreshold | 1 | Readiness only: consecutive passes before a withdrawn instance is restored to traffic. Must be 1 on a liveness probe. |
Since v0.0.1-dev.148. Before that, only exec probes honour timeoutSeconds; http and tcp always wait 5 seconds.
TCP probe
health:
liveness:
type: tcp
port: 5432
intervalSeconds: 5
timeoutSeconds: 2
Use TCP when there's no HTTP endpoint to hit — databases, message queues, custom protocols.
Designing a good probe
- Probe the dependency, not the framework. A
/healthzthat always returns 200 tells you nothing. A/healthzthat pings the DB and returns 503 if it can't is useful. - Be cheap. Probes run every 10s × N instances. Don't hit a slow query.
- Don't probe upstream services. If your probe checks "can I reach the auth API?" then a brief auth blip kills your whole service. Keep probe scope local.
- Set
initialDelaySecondsgenerously for slow-starting services — JVM, Rails, anything with warm-up. failureThreshold * intervalSecondsis your tolerance window. Default is 30s before kill — usually right.
Inspecting probe state
rune health api --checks
rune health instance api-instance-abc123 --checks
Output includes the most recent probe result, last failure message, and consecutive counts. A failing probe also logs a Liveness check failed / Readiness check failed warning on the server, with the failure under the reason field.
Since v0.0.1-dev.148. Before that, the reason is dropped from JSON logs.
When probes lie
The two failure modes:
- False negatives — your service is fine but probes fail. Cause: tight timeout, expensive endpoint, network blip. The reconciler kills working instances. Fix: relax timeouts, simplify the endpoint.
- False positives — your service is broken but probes pass. Cause: probe doesn't actually exercise the failing path. Fix: have
/healthztouch the things that actually matter (DB connection, cache, queue).
Liveness vs. readiness
A service can define two probes under health, and they answer different questions:
liveness— "is this process still working?" Repeated failures restart the instance (afterfailureThresholdconsecutive failures, default3, with restart backoff), unless the service setsrestartPolicy: never.readiness— "is this instance ready to take traffic yet?" It decides when a new instance becomesRunning.
health:
readiness:
type: http
path: /ready
port: 8080
initialDelaySeconds: 2
intervalSeconds: 5
liveness:
type: http
path: /healthz
port: 8080
initialDelaySeconds: 30
Readiness uses type, path/port, initialDelaySeconds, intervalSeconds and timeoutSeconds as described above, and runs independently of liveness — each probe has its own delay and interval. failureThreshold applies to both probes; successThreshold to readiness only.
What readiness does
- Without a readiness probe, an instance is
Runningas soon as its container starts. If the app needs 30 seconds to boot, it showsRunningand is routed to for those 30 seconds. - With one, a new instance stays
Starting("Waiting for readiness probe") and receives no traffic until the first readiness probe passes. Then it is promoted toRunningand added to the service's endpoints. - Updates are gated on it. With a readiness probe, a replacement counts as ready when the app is serving. Without one, Rune waits a flat 5 seconds per replacement, and
rune lintwarns (update-needs-readiness) for services that expose ports, unless the service setsupdateStrategy: recreateor is stateful (claimTemplatevolumes). - Dependencies use it. A service waits for each dependency to have at least one
Runninginstance; if the dependency defines a readiness probe, that instance's probe must also be passing.
When readiness fails
Once an instance is Running, a readiness probe that fails failureThreshold times in a row (default 3) withdraws it from traffic. The instance is not restarted and its status stays Running; it is marked not ready, a NotReady event is recorded, and it stays in the service's endpoint set marked unhealthy, so it gets no traffic. After successThreshold consecutive passes (default 1) it is restored and a Ready event is recorded.
rune get instancesshows the state in aREADYcolumn:yes,no, or-for an instance that isn'tRunning.rune get servicescounts only ready instances in itsREADY N/M.- There is no fail-open. If every replica is not ready, the service has no healthy endpoints until one recovers.
- During a rolling update a not-ready instance doesn't count as serving, and the minimum-ready window restarts when it is ready again. A dependency outage therefore can't retire every replica at once: outdated not-ready instances are retired at most one at a time (or as many as the update's allowed dip, set by
minServing), paced by replacements becoming ready. - Instances without a readiness probe are always treated as ready.
Only a failing liveness probe restarts an instance. successThreshold doesn't apply there: one pass clears liveness failures, and a liveness probe that sets successThreshold above 1 is rejected with invalid health check: invalid liveness probe: successThreshold must be 1; it applies to readiness only.
On dev; ships after v0.0.1-dev.148. Until then a failing readiness probe only gates the move to Running, successThreshold is ignored, and rune get instances has no READY column.
Choosing between them
Point readiness at "can I serve a request right now?" (dependencies reachable, caches warm) and liveness at "is the process wedged?" (a cheap check). A liveness probe that checks external dependencies turns a database outage into a restart loop for every instance.