A probe result is evidence consumed by a control loop. Kubernetes does not infer your application’s recovery strategy from a route named healthz. The probe type, result, configured thresholds, and container lifecycle determine what happens next. Treating every failure as “restart the Pod” erases distinctions that directly affect availability.
Three questions, different actions
A startup probe gives initialization its own detection policy. When configured, it prevents liveness and readiness probes from running until it succeeds. It stops checking after success for that container lifetime; a newly started container needs to pass startup again.
Liveness is intended to identify conditions for which restarting the affected container can help. After the failure threshold, the kubelet kills that container and applies its restart policy. This is not a promise to replace the whole Pod, change its identity, or repair an unavailable external database.
Readiness controls whether the container is considered ready. A failed readiness check leaves the container running and continues checking for recovery. It affects ordinary readiness-aware Service routing, but is not a firewall and does not cancel requests already using a connection.
Thresholds operate on consecutive observations
A failureThreshold of three is not a three-failures-ever counter. A success breaks the run. Readiness can require multiple consecutive successes to recover; successThreshold must be one for startup and liveness. Know both directions of a transition before interpreting a short trace.
Timeouts, initial delays, periods, and termination grace also matter. Threshold multiplied by period is useful for planning, not an exact end-to-end failover stopwatch. Scheduling, probe execution, and routing propagation intervene. Readiness may be checked more frequently than its configured period while a container is not ready.
The experiment uses four startup failures, three liveness failures, and two readiness failures, with two readiness successes required for recovery. These settings make counters observable; they are not production recommendations.
Match the mechanism to the evidence
An HTTP check uses the documented 200–399 success range. A 200 response containing an error message is not a failed HTTP probe merely because a human dislikes its body. Verify redirects and authentication on the actual probe route rather than assuming the same behavior as an interactive browser.
A TCP check shows that a connection can be opened, not that a query or transaction succeeds. An exec check needs a command present in the container and a meaningful exit status; frequent process creation also costs resources. Select the cheapest mechanism that answers the required question.
Readiness is not a prerequisite for liveness. Without startup gating, a long initialization period can repeatedly fail liveness before the application has time to become ready. Conversely, an excessively generous startup allowance can postpone detection of a genuinely broken launch.
When it breaks
Using external dependency availability as liveness can restart healthy processes throughout a shared outage. An always-green handler on an unrelated thread can miss a deadlocked business worker. Restarts are not automatically cheap: they can lose in-flight work and repeat expensive initialization.
A readiness gate covering every optional dependency can withdraw useful capacity. Rapid state flapping can transfer load between replicas and worsen saturation. Check false positives and recovery behavior, not just whether a deliberately broken endpoint turns red.
Compare probe outcomes with container restart counts, events, readiness transitions, and actual client traffic. Confirm the deployed configuration and routing consumer. A successful local policy model cannot establish that a real kubelet, network path, or load balancer applies it correctly.
Check it quickly
Save this block as probe_policy_check.py and run python3 probe_policy_check.py with Python 3.10 or newer. It ran on Python 3.14.4. This deterministic model consumes binary observations; it does not deploy a Pod, call Kubernetes, schedule probes, or restart an operating-system process.
class ProbePolicy:
# (consecutive successes, consecutive failures), chosen for this experiment.
thresholds = {"startup": (1, 4), "liveness": (1, 3), "readiness": (2, 2)}
def __init__(self):
self.restart_requests = 0
self.start_container()
def start_container(self):
self.started = self.ready = self.restart_requested = False
self.successes = dict.fromkeys(self.thresholds, 0)
self.failures = dict.fromkeys(self.thresholds, 0)
def sample(self, kind, passed):
if self.restart_requested:
return "awaiting-restart"
if kind != "startup" and not self.started:
return "suppressed"
if kind == "startup" and self.started:
return "disabled"
success_limit, failure_limit = self.thresholds[kind]
self.successes[kind] = self.successes[kind] + 1 if passed else 0
self.failures[kind] = 0 if passed else self.failures[kind] + 1
if passed and self.successes[kind] >= success_limit:
if kind == "startup":
self.started = True
elif kind == "readiness":
self.ready = True
if not passed and self.failures[kind] >= failure_limit:
self.ready = False
if kind != "readiness":
self.restart_requested = True
self.restart_requests += 1
return "restart-requested" # A model output, not a kubelet or OS action.
return "sampled"
policy = ProbePolicy()
for _ in range(3):
assert policy.sample("startup", False) == "sampled"
assert policy.sample("liveness", False) == "suppressed"
assert policy.sample("readiness", True) == "suppressed"
assert policy.restart_requests == 0 and not policy.ready
assert policy.failures["liveness"] == 0 and policy.successes["readiness"] == 0
policy.sample("startup", True)
assert policy.started and not policy.ready
assert policy.sample("startup", False) == "disabled"
print("three startup failures do not trigger liveness; startup success enables the other probes")
policy.sample("readiness", True)
assert not policy.ready
policy.sample("readiness", True)
assert policy.ready
policy.sample("readiness", False)
assert policy.ready
policy.sample("readiness", True) # Breaks the consecutive-failure run.
policy.sample("readiness", False)
assert policy.ready
policy.sample("readiness", False)
assert not policy.ready and policy.restart_requests == 0
for result in (True, False, True):
policy.sample("readiness", result)
assert not policy.ready
policy.sample("readiness", True)
assert policy.ready
print("two readiness failures remove readiness without restart; two consecutive successes restore it")
for result in (False, False, True, False, False):
policy.sample("liveness", result)
assert policy.restart_requests == 0
assert policy.sample("liveness", False) == "restart-requested"
assert policy.restart_requests == 1 and not policy.ready
assert policy.sample("liveness", False) == "awaiting-restart"
assert policy.restart_requests == 1
policy.start_container() # Explicitly model the beginning of another container lifetime.
assert not policy.started and not policy.ready
for _ in range(4):
policy.sample("startup", False)
assert policy.restart_requests == 2
print("three consecutive liveness failures request a restart; a fresh lifetime runs startup checks again")
print("four startup failures request another restart; no real container, clock, or traffic route was changed")
Three startup failures remain below the fixture’s threshold, and attempted liveness/readiness observations are suppressed. Startup success enables those checks without automatically declaring readiness.
Two consecutive readiness failures remove readiness without requesting a restart; recovery needs two consecutive successes. Intervening opposite outcomes reset the relevant streak. Three consecutive liveness failures produce one restart request, not repeated requests while awaiting restart.
An explicitly modeled new container lifetime clears startup state. Four startup failures then request another restart. Unknown results, transport error classification, real timing, restart policy execution, termination grace, and endpoint propagation remain untested; validate those on the intended cluster before relying on the full operational behavior.
Further reading
Sources
- Kubernetes: Liveness, readiness, and startup probes, probe actions, mechanisms, thresholds, and startup gating, checked 2026-09-09.
- Kubernetes: Configure probes, executable probe configuration and observed container restarts, checked 2026-09-09.
- Kubernetes: Pod lifecycle, container restart policy and termination versus traffic lifecycle, checked 2026-09-09.