Deciding a server is dead  / field guide
Practitioner field guide · 2026-08-31

Deciding a server is dead

Every distributed system runs a machine whose job is to declare other machines dead and act on the verdict without asking anyone. This guide reconstructs how that machine is actually built, from six published incidents, five production codebases, four papers and two decades of design records, and lands on the rule the survivors converged on: the detector gets a budget for being wrong, and the budget is enforced on the action, not the signal.

30 primary sources 12 production systems 6 published incidents Evidence through Oct 2025 Read: 25 min
01

The territory

The problem, stated without naming a product: from incomplete signals, decide whether a peer that stopped answering is dead or merely slow, then act on that verdict automatically, without the action making the situation worse.

50%
Healthy-host floor below which Envoy stops believing its own health data (default panic threshold)
10%
Maximum share of a cluster Envoy's passive detection may eject, by default
43 s
Network partition that triggered automated failover and 24 h 11 m of degraded service at GitHub
50×
Reduction in false dead verdicts when a detector also scores its own health (Lifeguard, at β=6)

On 20 October 2025, hours after the DynamoDB DNS record that triggered the incident had been repaired, AWS was still losing capacity in us-east-1. The reason, according to AWS's own postmortem, was the Network Load Balancer's health-check subsystem: checks "alternated between failing and healthy, which caused NLB nodes and backend targets to be removed from DNS, only to be returned to service when the next health check succeeded." The servers were fine. The thing deciding whether they were fine was not, and it was wired directly to the lever that removes capacity. That wiring is the subject of this guide.

A failure detector can be wrong in two directions, and the costs are wildly asymmetric. A false negative leaves a dead server in rotation; David Yanacek's account in the Amazon Builders' Library (2019) explains why this is worse than it sounds: a server that fails requests fast attracts more traffic under least-requests balancing, "creating a 'black hole' in the service fleet." A false positive removes a healthy server, which is cheap, once. The disaster case is the correlated false positive: one shared cause (a dependency blip, a saturated network, a bug in the checker itself) makes every server fail its check in the same minute, and an unbudgeted actor removes them all. Five of the six incidents in this guide are that shape.

The finding that surprised me

Between 2012 and 2025, at least five independent teams converged on the same guardrail under five different names: Netflix's Eureka stops expiring registrations when renewals drop below 85% ("self-preservation", in the code since 2012); Envoy caps passive ejection at 10% of a cluster and ignores health data entirely below 50% availability ("panic threshold"); HAProxy ships the same idea, and Slack credits it by name ("panic mode") for turning their January 2021 outage from down into degraded; Route 53, ALB and NLB "fail open" when zero targets are healthy; and AWS's October 2025 remediation adds "velocity control" to limit how fast health checks can remove NLB capacity. Five names, one rule: cap what the failure detector may do before you trust what it says. The most widely deployed health checker on earth, the kubelet, has no such cap; the 2018 issue asking for one (kubernetes/kubernetes#66230) is still open, frozen.

Figure 1 · The landscape: four signal families, one verdict, one dangerous lever

restarts add load;
eviction concentrates it

Active probe
(LB checks, kubelet)

Verdict:
dead or slow?

Passive outcomes
(errors on real traffic)

Self-report
(lame duck, NOT_SERVING)

Peer gossip
(SWIM, Consul, Cassandra)

Action gate
(the budget)

Evict, restart,
or fail over

restarts add load;
eviction concentrates it

Active probe
(LB checks, kubelet)

Verdict:
dead or slow?

Passive outcomes
(errors on real traffic)

Self-report
(lame duck, NOT_SERVING)

Peer gossip
(SWIM, Consul, Cassandra)

Action gate
(the budget)

Evict, restart,
or fail over

Every production design in this guide is an arrangement of these parts; the feedback edge is what turns a wrong verdict into an incident. Reconstructed from AWS, Envoy, Google SRE and SWIM.
Diagram source

Scope: this guide covers failure detection between servers inside a system, meaning health checks, probes, gossip-based membership and the automated actions wired to them. It deliberately does not cover human-facing monitoring and alerting, consensus-protocol correctness, or what the clients of a failing service should do; that last problem (retries, backoff, metastable overload) has its own dig in this collection. Where the record runs out, the page says so: the most conspicuous gap is named in section 04.

02

How it is actually built

The common shape across AWS, Google, Envoy, Kubernetes, Consul, Cassandra and Eureka: signals feed a suspicion score, the score crosses a threshold, and then, in every mature design except one, the resulting action passes through a gate that limits how much the detector may do at once.

Figure 2 · Reference architecture of a failure-detection plane

4 · Act

3 · Gate (the budget)

2 · Judge

1 · Observe

cold start makes the
next check fail too

Active checks
NLB, HAProxy, kubelet

Passive outcomes
Envoy outlier detection

Self-report
Google lame duck, gRPC health

Suspicion accumulator
3 consecutive fails, or phi accrual

max ejection 10% · panic at 50%
self-preservation at 85% · fail open at 0

Stop routing to it

Restart it

Fail over / re-elect

4 · Act

3 · Gate (the budget)

2 · Judge

1 · Observe

cold start makes the
next check fail too

Active checks
NLB, HAProxy, kubelet

Passive outcomes
Envoy outlier detection

Self-report
Google lame duck, gRPC health

Suspicion accumulator
3 consecutive fails, or phi accrual

max ejection 10% · panic at 50%
self-preservation at 85% · fail open at 0

Stop routing to it

Restart it

Fail over / re-elect

The gate is the divergence point: Envoy, Eureka, HAProxy and the AWS load balancers have one; the kubelet acts on each verdict unconditionally. Attributions in the component cards below.
Diagram source

Observe. Four signal families recur. Active probes are a synthetic request on a timer: the NLB and HAProxy model, and the kubelet's liveness, readiness and startup probes. Passive detection watches real traffic instead: Envoy's outlier detection ejects a host on consecutive 5xx or on statistical deviation from the cluster's success rate, which means it costs nothing extra and sees what users see, but also means it is blind on idle clusters. Self-reporting inverts the direction: Google's RPC servers enter a "lame duck" state in which, per the SRE book's load-balancing chapter, the task "is listening on its port and can serve, but is explicitly asking clients to stop sending requests"; gRPC standardised the same move as the health-checking protocol's NOT_SERVING status. And peer gossip makes every node a checker of a few random others: SWIM and its descendants in Consul's memberlist and Cassandra's gossiper, adopted because, as the SWIM paper (2002) argues, central all-to-all heartbeating imposes network load that grows quadratically with group size.

Judge. The naive judgment is a counter: Kubernetes convicts after 3 consecutive probe failures, 10 seconds apart, 1-second timeout, all defaults readable in types.go. The refined judgment is a continuous suspicion score: Cassandra's FailureDetector.java implements Hayashibara's 2004 phi accrual detector, which models heartbeat inter-arrival times as a distribution and emits a suspicion value that rises smoothly as silence grows, convicting at a configurable phi (default 8 in cassandra.yaml). The difference matters on jittery networks: a fixed timeout encodes one assumption about latency; phi re-learns the assumption continuously. The 2017 Lifeguard paper from HashiCorp adds the judgment nobody else had written down: the checker should also suspect itself. Serf and Consul's memberlist scores its own responsiveness (an "awareness" counter in the code) and lengthens its probe timeouts when it is the degraded party; the paper measures a reduction of false positives "by over 98%" at the median-latency-neutral setting. Slow message processing at the observer, not death at the observed, was most of the noise.

Gate, then act. This is the layer this guide exists to point at, and the one I have to name myself because every source names it differently: call it the eviction budget. Envoy enforces it twice, as a per-sweep cap (at most 10% of a cluster ejected by outlier detection, per the proto default) and as a global floor (below 50% available hosts the load balancer "will disregard health status", with the docs stating the purpose plainly: "to avoid a situation in which host failures cascade throughout the cluster as load increases"). Eureka's registry stops expiring instances entirely when fewer than 85% of expected heartbeat renewals arrive, because, per the code's own javadoc, "eureka perceives this as a danger", the danger being a network problem masquerading as mass death. The AWS load balancers fail open at zero healthy targets. Each of these is the same statement in different syntax: past a threshold of agreement, the dead verdicts stop being information about servers and start being information about the detector, so the action lever locks.

Figure 3 · Host lifecycle as the gate sees it

check fails / phi rises

success before threshold

convicted AND budget left

convicted, budget spent

cluster recovers

ejection time up (add jitter)

passes

fails, backoff doubles

Healthy

Suspected

Ejected

ServedAnyway

Probation

check fails / phi rises

success before threshold

convicted AND budget left

convicted, budget spent

cluster recovers

ejection time up (add jitter)

passes

fails, backoff doubles

Healthy

Suspected

Ejected

ServedAnyway

Probation

The two transitions out of "Suspected" are the design: conviction spends budget, and an exhausted budget routes traffic to suspects anyway. From Envoy's outlier proto (ejection backoff, return jitter) and the panic-threshold doc.
Diagram source

Where each system diverges: who checks

Central checker fleet: NLB, Route 53. Every client checks for itself: Envoy, HAProxy, gRPC client-side health (design record A17, 2018). The node's own agent: kubelet. Random peers: SWIM, Consul, Cassandra. Per-client checking gives each client a private, partition-tolerant view; central checking gives one consistent view and one correlated failure mode, which is what flapped in the AWS October 2025 incident.

Where each system diverges: what a verdict triggers

Stop routing (all load balancers), restart the process (kubelet liveness only), or fail over state (GitHub's Orchestrator, Consul leader election). The severity ladder matters because only the first action is cheaply reversible. GitHub's 2018 postmortem is the canonical case of a correct verdict wired to a hard-to-reverse action.

The known blind spot: gray failure

Microsoft's HotOS 2017 paper names the failure class every binary check misses: "differential observability", where "the system's failure detectors may not notice problems even when applications are afflicted by them". Roblox's 73-hour outage is this exactly: leaders that were alive, checkable and useless. Azure's operational answer (SREcon24 talk below) is to compare the app's view with the checker's view instead of trusting either.

03

The decisions that matter

Four forks, each with the condition that flips it. The pattern across all four: the more powerful the action, the dumber the check should be, and the tighter the budget.

How deep may the check look: process only, or dependencies too?

Chosen
  • Shallow checks gate destructive actions: Kubernetes docs, Colin Breck and Henning Jacobs all converge on liveness probes that touch nothing off-box
  • Dependency ("deep") checks only where the actor fails open: Yanacek describes AWS running deep checks behind fail-open load balancers
Rejected
  • Deep liveness checks. Breck: a liveness probe with an external dependency is "the worst situation", one database hiccup restarts every container
  • Encore's account shows the readiness version: an auth-service failure removed every pod from the load balancer at once
Flips when
  • The actor is budgeted and fails open. Behind Route 53 or NLB fail-open, a deep check adds coverage without the fleet-wide kill, which is exactly the AWS position
  • The dependency is genuinely per-instance (a local disk, a sidecar), because then failures cannot correlate across the fleet

Binary timeout, or continuous suspicion?

Chosen
  • Long-lived peer systems buy the complexity: Cassandra ships phi accrual with convict-at-8; Akka ports the same detector
  • Consul adds Lifeguard's self-suspicion on top of SWIM, cutting false positives more than 50× in the paper's evaluation
Rejected
  • Fixed timeouts for peer membership, rejected by the phi paper because one constant encodes one network condition
  • But note the counter-choice: every load balancer and the kubelet still uses plain consecutive-failure counters (3 × 10 s default in Kubernetes)
Flips when
  • The check path is short and controlled (LB to backend in one datacenter): counters are fine, and their behaviour is explainable at 3 a.m.
  • The path crosses noisy networks or the observer itself can degrade: pay for phi and for self-suspicion, or eat Lifeguard's 20–50× noise

What happens when everyone looks dead?

Chosen
  • Fail open: Route 53, ALB, NLB (Yanacek), HAProxy panic mode (credited by Slack's January 2021 postmortem), Envoy's default panic behaviour, Eureka self-preservation
  • Google's June 2025 remediation moves Service Control the same way: "fail open rather than closed"
Rejected
  • Fail closed as the default, because mass agreement among checks is more often a checker or network event than mass death
Flips when
  • Envoy's own docs state the flip: fail closed if the upstream "is observed to fail in an all-or-nothing pattern", because then sending traffic only deepens the hole
  • Serving wrong is worse than serving nothing (split-brain writes, stale auth): GitHub chose data integrity over availability and took the 24-hour recovery
DecisionChosen byRejectedBecauseEvidence
Cap the detector's removal rateEnvoy (10%/sweep), Eureka (85% floor), AWS NLB (velocity control, being added)Unconditional per-verdict actionCorrelated verdicts indict the checker, not the fleetAWS, 2025
Probe depth for kill actionsKubernetes docs, Zalando, Breck: process-local onlyDeep livenessA dependency blip restarts the whole fleet at oncek8s docs
Slow startersstartupProbe (KEP-950, v1.16+)initialDelaySeconds; huge failureThresholdBoth trade away deadlock detection; the KEP says so explicitlyKEP-950
Planned exitsSelf-report: lame duck, gRPC NOT_SERVINGLetting the checker discover the drainPropagates in "1 or 2 RTT" instead of a probe period, zero errorsSRE book
Membership at scaleGossip with suspicion (SWIM lineage)Central all-to-all heartbeatsQuadratic network load, or worse detection latencySWIM, 2002
Client-side health for gRPCReuse server health service per-connectionLook-aside balancer as sole checkerNot every deployment has a balancer in the pathgRPC A17, 2018

Figure 4 · Choosing check depth and budget: the tree the sources imply

yes (liveness-like)

no, routing only

yes

no

yes

no

Does a failed check
kill or restart the target?

Check the process only.
No dependencies, ever.

Does the actor cap removals
and fail open past the cap?

Deep checks are safe:
local + dependency probes

Add the budget first;
until then, stay shallow

Slow starter?

startupProbe gates liveness
(KEP-950 pattern)

Counter thresholds are fine:
3 fails × 10 s

yes (liveness-like)

no, routing only

yes

no

yes

no

Does a failed check
kill or restart the target?

Check the process only.
No dependencies, ever.

Does the actor cap removals
and fail open past the cap?

Deep checks are safe:
local + dependency probes

Add the budget first;
until then, stay shallow

Slow starter?

startupProbe gates liveness
(KEP-950 pattern)

Counter thresholds are fine:
3 fails × 10 s

Terminal nodes are actions. The left path is the one the Kubernetes probe folklore (Jacobs, Breck, the docs caution) exists to enforce; the right path is Yanacek's argument for deep checks behind fail-open.
Diagram source
04

What broke in production

Six published incidents, in three classes: the detector condemned the fleet; the detector believed the fleet; the detector was right and the action was still the outage.

Class 1 · Correlated false positives: the check condemned the fleet

Postmortem

AWS us-east-1: the checker removed the capacity

AssumptionA failed health check means the target is unhealthy.
What happenedDuring the October 2025 recovery, the NLB health-check subsystem was itself degraded; checks "alternated between failing and healthy", repeatedly pulling NLB nodes and backend targets out of DNS and putting them back.
Blast radiusConnection errors across NLB-fronted services for hours inside a 14.5-hour regional event (11:48 p.m. Oct 19 to 2:20 p.m. Oct 20 PDT).
FixAWS is "adding a velocity control mechanism to limit the capacity a single NLB can remove when health check failures cause AZ failover".
Design ruleRate-limit the remover. A health checker needs a removal budget per unit time, sized so its worst hour cannot exceed the fleet's redundancy.
Blog

Encore: one auth dependency, zero pods in rotation

AssumptionA pod that cannot reach the auth service is not ready, so removing it protects users.
What happenedDeep readiness checks queried the auth dependency; when it failed, every pod failed the check in the same window: "all of our pods being removed from the load balancer for our service; we have a complete outage."
Blast radiusTotal outage of a service whose own processes were healthy and could have served degraded responses.
FixStop failing readiness on shared dependencies; report dependency health as telemetry instead of a verdict.
Design ruleA check whose failure can correlate across the fleet must not gate an ungated actor. Shared-dependency status belongs in metrics, not in the routing decision.

Class 2 · The detector believed the fleet: gray failure and stale maps

Postmortem

Roblox: 73 hours of leaders that were alive and useless

AssumptionA Consul leader that passes health checks and wins elections can lead.
What happenedA new streaming path put "contention on a single Go channel" under combined read and write load; servers were reachable but slow. Leadership flapped among nodes with the same latent defect; the team eventually had to "prevent the problematic leaders from staying elected" by hand.
Blast radius73 hours of full outage, October 28–31, 2021; recovery gated behind DNS steering that let players back in percentage by percentage.
FixStreaming disabled, slow leaders blocked from election, then structural work on isolation and observability of the coordination layer.
Design rule"Alive" is not a capability claim. Anything that wins elections or takes traffic on the strength of a health check needs a performance signal in the verdict, not just reachability.
Postmortem

Slack, May 2020: the map of who is alive aged out

AssumptionThe load balancer's list of healthy backends tracks the fleet.
What happenedHAProxy instances carried stale server state: "most HAProxy instances were only able to send requests to older webapp backends", many "more than eight hours old". When evening autoscaling terminated those older backends, the stale map pointed mostly at corpses.
Blast radiusA user-visible outage at the end of the US business day, on top of an earlier degradation.
FixRepair of the state-propagation pipeline and, longer term, Slack's migration of this tier to Envoy.
Design ruleLiveness data is perishable. Age it, alert on its staleness, and treat "the map has not changed in hours" as a failure of the detection plane itself.

Class 3 · The verdict was right; the action was the outage

Postmortem

GitHub: 43 seconds of partition, 24 hours of consequences

AssumptionIf the primary datacenter is unreachable, failing the databases over to the other coast is the safe reflex.
What happenedRoutine maintenance cut the East–West link for 43 seconds. Orchestrator's quorum, correctly observing the partition, "began a process of leadership deselection" and promoted West Coast primaries; East Coast writes from those seconds now existed on only one side.
Blast radius24 hours 11 minutes of degraded service while databases were reconciled; no data lost, but hours of manual work.
FixDeliberate limits on automated cross-country failover and investment in the topology the automation assumed.
Design ruleMatch the action's reversibility to the verdict's confidence window. A 43-second signal must not be allowed to trigger an action that takes a day to undo without a human or a delay in the loop.
Postmortem

Google Cloud: the mandatory checker with no fail-open

AssumptionService Control, the binary that must approve every API request, will not itself become the dead component.
What happenedOn 12 June 2025 a policy row with blank fields hit an unflagged code path; Service Control crash-looped in every region within seconds because the data replicates globally. Every check it could not run became a rejected request: fail closed, planet-wide.
Blast radiusDozens of Google Cloud and Workspace services, roughly 7.5 hours end to end; the "red-button" disable took about 40 minutes to roll out.
FixCommitted remediations include modularizing Service Control "to fail open rather than closed" and mandatory feature flags for such changes.
Design ruleAny checker that sits mandatorily in the request path is a health check on the whole platform. It needs the same fail-open past a threshold that a load balancer gives its backends.
The counter-example that proves the budget

Slack's January 4, 2021 postmortem is the same setup as Class 1 with the opposite ending. AWS network saturation made health checks fail en masse against healthy web instances, exactly the correlated false positive. But Slack's HAProxy tier had "a feature called 'panic mode' which balances requests across all instances when many are failing health checks". The detector was just as wrong as AWS's NLB checker in 2025; the budget refused to act on it, and Slack spent the morning degraded instead of dark. Same failure, one design difference, different day.

Figure 5 · The deep-check cascade, as a sequence

kubelet x NPod fleetShared dependencykubelet x NPod fleetShared dependency20-second hiccupcold caches, reconnect storm,CPU throttlingthe detector keeps refuting therecoveryliveness handlers query ittimeouts3 failures in 30 s, verdict on everynodeSIGKILL + restart, fleet-wideprobes now miss the 1 s defaulttimeoutrestart again (CrashLoopBackOff)
kubelet x NPod fleetShared dependencykubelet x NPod fleetShared dependency20-second hiccupcold caches, reconnect storm,CPU throttlingthe detector keeps refuting therecoveryliveness handlers query ittimeouts3 failures in 30 s, verdict on everynodeSIGKILL + restart, fleet-wideprobes now miss the 1 s defaulttimeoutrestart again (CrashLoopBackOff)
The kubelet enforces each verdict with no fleet-wide view, so a 20-second dependency blip becomes a fleet restart and then a restart loop. Corroborated by issue #66230, Breck and HeyOnCall's throttling analysis.
Diagram source

One absence is worth stating plainly. Among these six accounts, and in the wider corpus behind this page, no operator postmortem attributes an incident to the failure mode health checks nominally exist for: a genuinely dead server left in rotation because nothing noticed. Yanacek describes the class from inside Amazon (his opening example is a single server whose disk froze overnight and which "black-holed" its share of traffic), but as a war story motivating checks, not a published incident. Read that two ways at once: basic detection is a solved problem, and the residual risk has moved entirely into the reaction. The published record says you are far more likely to be taken down by your health checks than saved from a downed server by them, because the saves are silent and the misfires make the news.

05

Numbers you can plan against

Defaults you will inherit, budgets others chose, and what wrong verdicts have cost. Measured and reported figures only; anything derived says so.

MetricValueAtContextAs ofSource
Default panic threshold50%EnvoyBelow this availability, health data is disregarded2026docs
Default max ejection10%EnvoyCap on cluster share removable by outlier detection2026proto
Self-preservation floor85%Netflix EurekaRenewal rate below which expiries stop entirely2026DefaultEurekaServerConfig
Default probe verdict3 × 10 s, 1 s timeoutKubernetesConsecutive failures, period, and the timeout that bites under CPU throttling2026types.go
Conviction thresholdphi = 8CassandraContinuous suspicion score; "most users should never need to adjust this"2026cassandra.yaml
False-positive reduction from self-suspicion>98% (50×)Lifeguard evalAt β=6, median detection latency unchanged2017paper
Lame-duck propagation1–2 RTTGoogleSelf-reported drain reaches all clients, vs a probe period for discovery2016SRE book
Partition that triggered failover43 sGitHubAutomated promotion; degraded service for 24 h 11 m2018postmortem
Gray-failure outage73 hRobloxEvery node alive; leadership flapping among slow leaders2021postmortem
Regional event window~14.5 hAWS us-east-111:48 p.m. Oct 19 to 2:20 p.m. Oct 20 PDT; NLB check flapping inside it2025postmortem
Fail-closed checker outage~7.5 hGoogle CloudCrash-looping Service Control; ~40 min to roll the red-button disable2025incident report

Two derived numbers worth doing on your own fleet. First, worst-case removal rate: with Kubernetes defaults, a correlated probe failure kills every affected pod in about 30 seconds (3 failures × 10-second period); your eviction budget is whatever survives that. Second, detection floor: an active checker cannot distinguish dead from slow faster than its timeout, and the Kubernetes default timeout is 1 second, which is why CPU throttling reads as death. Neither number appears in any dashboard by default.

Read these carefully

Configuration defaults were read from the projects' current source trees on 2026-08-31 and can drift; incident figures are the operators' own reported numbers, not independent measurements. This page was researched from an environment that could fully fetch GitHub-hosted sources but had to verify web-hosted postmortems through live search results rather than full page fetches; the ledger (sources.md, shipped beside this page) marks which is which, row by row.

06

The evidence wall

Every source behind this page, graded. Postmortems and source code outweigh commentary; vendor documentation is labelled as such. Filter by kind.

Postmortem AWS2025-10

Summary of the DynamoDB service disruption in US-EAST-1

The definitive recent instance of a health checker removing healthy capacity: NLB checks flapped during recovery, pulling nodes and targets from DNS repeatedly.

Carry forwardThe remediation is a removal-rate budget ("velocity control"), added in 2025 to a mechanism Envoy has shipped as a default since the 2010s.
aws.amazon.com/message/101925
Postmortem Slack2021-02

Slack's outage on January 4th 2021

Mass health-check failures against healthy instances during AWS network saturation; HAProxy's panic mode ignored the checks and kept balancing across all backends.

Carry forwardThe fail-open budget working as designed, credited by name in a production postmortem: degraded, not down.
slack.engineering
Postmortem Slack2020

A terrible, horrible, no-good, very bad day at Slack

HAProxy instances ran for hours with stale backend state; when autoscaling removed the old backends they still pointed at, the map failed before any server did.

Carry forwardTreat the liveness map as data with a freshness SLO; alert when it stops changing.
slack.engineering
Postmortem GitHub2018-10

October 21 post-incident analysis

A 43-second partition, a correct quorum decision, an automated cross-country promotion, and 24 hours of reconciliation because writes existed on both sides.

Carry forwardBound automation by the reversibility of its action, not the confidence of its detection.
github.blog
Postmortem Roblox2022-01

Return to service, 10/28–10/31 2021

The canonical gray-failure outage: contention made Consul servers slow rather than dead, health checks kept passing, and leadership flapped among defective leaders for days.

Carry forwardPut a performance signal in any verdict that awards leadership or traffic.
about.roblox.com
Postmortem Google Cloud2025-06

Service Control incident report, June 12 2025

A mandatory fail-closed checker crash-looped on bad global data; every API request it could not evaluate was rejected. Remediations: fail open, feature flags, staged data propagation.

Carry forwardA checker in the mandatory path is a platform health check and needs a platform fail-open.
status.cloud.google.com
Source Envoy2026 tree

outlier_detection.proto and the panic-threshold doc

The budget as shipped defaults: 10% max ejection per cluster, 50% panic floor, ejection backoff with optional return jitter "to prevent a 'thundering herd' effect".

Carry forwardThe configuration surface is the operational wisdom; every field exists because something happened.
envoyproxy/envoy
Source Kubernetes2018–

Issue #66230: prevent mass livenessProbe failures from taking down all pods

The eviction budget, proposed for the kubelet (honour PodDisruptionBudget before acting on probe failures), discussed, labelled a feature, and frozen. Kubernetes' missing gate has an eight-year-old paper trail.

Carry forwardIf the platform will not budget the actor, you must budget the check: keep liveness shallow and rare.
kubernetes/kubernetes#66230
Source Kubernetes2026 tree

Probe defaults in types.go

1-second timeout, 10-second period, 3 consecutive failures. The 1-second default is the load-bearing surprise: a CPU-throttled pod misses it while doing useful work.

Carry forwardAudit inherited defaults against your own p99 under load, not against a healthy pod.
kubernetes/kubernetes
Source Apache Cassandra2026 tree

FailureDetector.java and cassandra.yaml

A production phi accrual implementation, header citing Hayashibara, convicting when the scaled phi crosses phi_convict_threshold (default 8, "most users should never need to adjust this").

Carry forwardContinuous suspicion is not exotic; it has shipped in a mainstream database since the 2000s.
apache/cassandra
Source HashiCorp2026 tree

memberlist: awareness.go

Lifeguard in production Go: an "awareness" score of the local node's own health, used to scale probe timeouts so a sick observer accuses fewer healthy peers.

Carry forwardBefore convicting a peer, ask whether the evidence would also be produced by your own degradation.
hashicorp/memberlist
Source Netflix2026 tree

Eureka: self-preservation in the registry code

When renewals fall below renewalPercentThreshold (default 0.85), the registry "perceives this as a danger and stops expiring instances", per the javadoc; mass silence is read as a network event, not mass death.

Carry forwardThe oldest budget in this corpus, in the code since the early 2010s.
Netflix/eureka
Source Envoy2021

Issue #17650: outlier detection vs admission control

The community thread on what the budget feels like from inside: ejection proceeds until the cap, and past the panic threshold requests fail together rather than one host at a time.

Carry forwardKnow your gate's behaviour at the boundary before an incident makes you learn it live.
envoyproxy/envoy#17650
ADR Kubernetes2019–2020

KEP-950: startupProbe

The design record for the check that kills slow starters, with both prior workarounds rejected for stated reasons: initialDelaySeconds "delays deadlock detection", a high failureThreshold forfeits timely kills after startup.

Carry forwardWhen one probe must serve two phases of life, split the probe rather than tune the constants.
KEP-950
ADR Kubernetes2021

KEP-2238: probe-level termination grace period

Motivated by a reported outage: an ingress controller with a 3600-second drain grace wedged, and the liveness kill waited the full hour, "the worst possible outcome".

Carry forwardThe kill path and the drain path need separate clocks; sharing one betrays whichever purpose loses.
KEP-2238
ADR gRPC2018-08

A17: client-side health checking

Mark Roth's design for clients consuming the server's self-reported health, with the motivating case spelled out: a server that is up while a dependency "is not available", and alternatives considered in the record.

Carry forwardHealth is an application-level protocol; a healthy TCP accept proves almost nothing.
grpc/proposal A17
Blog AWS (David Yanacek)2019-12

Implementing health checks (Amazon Builders' Library)

The clearest operator statement of the core tension: thorough checks catch more, and "the harm done by a false positive failure across the entire fleet" caps how much you may act on them. Names fail-open and the black-hole effect.

Carry forwardDeep checks and destructive actions are each fine; the combination is the outage.
aws.amazon.com/builders-library
Blog Google SRE book2016

Load balancing in the datacenter (lame duck state)

The self-report pattern: a draining backend keeps serving but broadcasts "stop sending", reaching all clients in one or two round trips instead of a probe interval.

Carry forwardPlanned deaths should never be discovered; a server that knows it is leaving must say so.
sre.google
Blog Colin Breck2019

Kubernetes probes: how to avoid shooting yourself in the foot

The practitioner mechanics of probe-induced outages, including the sharpest sentence in the folklore: liveness plus an external dependency means "a single database hiccup will restart all containers".

Carry forwardLiveness answers "is this process wedged", nothing else; readiness carries the nuance.
blog.colinbreck.com
Blog Henning Jacobs (Zalando)2019

Liveness probes are dangerous

The post that made the danger common knowledge: most workloads need no liveness probe at all, and a wrong one subtracts availability.

Carry forwardThe burden of proof sits on adding the probe, not on omitting it.
srcco.de
Blog Encore2023

Distributed systems horror stories: Kubernetes deep health checks

A firsthand correlated-false-positive outage: readiness checks on an auth dependency removed every pod from the load balancer simultaneously.

Carry forwardReport dependency health; do not let it vote on routing for the whole fleet at once.
encore.dev/blog
Blog Michal Drozdrecent

Envoy outlier detection brownouts

Field notes on the budget misfiring in the small: with tiny endpoint pools and aggressive retries, ejection storms brown out a mesh service even inside the 10% cap.

Carry forwardPercentage budgets need absolute floors; 10% of three pods rounds to a third of your capacity.
michal-drozd.com
Paper Microsoft Research2017-05

Gray failure: the Achilles' heel of cloud-scale systems (HotOS)

Defines differential observability from Azure incident data: detectors and applications systematically disagree about health, and the disagreement is where major outages live.

Carry forwardMeasure the gap between the checker's verdict and the application's experience; the gap is the risk.
microsoft.com/research
Paper Cornell (Das, Gupta, Motivala)2002

SWIM: scalable weakly-consistent infection-style membership (DSN)

Replaces quadratic heartbeating with random probing, indirect probes, and a suspicion state before conviction; the ancestor of Consul, Serf and Uber's Ringpop membership.

Carry forwardSuspicion-before-conviction is the protocol form of the budget: a verdict you can appeal.
cs.cornell.edu
Paper Hayashibara et al.2004-10

The phi accrual failure detector (SRDS)

Suspicion as a continuous value on "a scale that is dynamically adjusted to reflect current network conditions", decoupling detection from any fixed timeout.

Carry forwardLet each consumer pick its own conviction threshold from one shared suspicion signal.
semanticscholar.org
Paper HashiCorp2017-07

Lifeguard: local health awareness for more accurate failure detection

Extends SWIM with the observer's self-suspicion; evaluation shows false positives cut by more than 98% at a setting that leaves median detection latency unchanged.

Carry forwardMost dead verdicts are produced by sick accusers; score the accuser.
arxiv.org/abs/1707.00788
Talk USENIX SREcon24 Americas2024-03-20

Gray failure in practice (Ryan Huang, U. Michigan; Ze Li, Microsoft Azure)

The 2017 paper's authors report how Azure operationalized differential observability, bridging "different components' perceptions of what constitutes failures". Slides published by USENIX.

Carry forwardSeven years paper-to-practice; the operational form is comparing views, not building a better single checker.
usenix.org
Talk The Downtime Project2021

GitHub's 43-second network partition (podcast)

Long-form independent walkthrough of the 2018 incident: what Orchestrator saw, what the automation did, and where a human in the loop would have changed the day.

Carry forwardReplaying someone else's failover decision at leisure is the cheapest failover training available.
downtimeproject.com
Vendor Kubernetes docscurrent

Liveness, readiness and startup probes

The official reference, carrying its own warning: "Incorrect implementation of liveness probes can lead to cascading failures", a caution that entered the docs after user pressure (kubernetes/website#16607).

Carry forwardWhen the vendor's own docs warn about the feature, believe them.
kubernetes.io
Vendor gRPC docscurrent

Health checking protocol

The standard SERVING / NOT_SERVING self-report service, letting servers "signal that they are not healthy without actually tearing down connections".

Carry forwardAdopt the standard protocol; ad hoc /healthz semantics are where depth mistakes hide.
grpc/grpc doc
07

Build a miniature, then productionise it

Six rungs from a toy checker to a detection plane you would trust. The line from toy to real is crossed at rung 4, where you give the detector a budget and then try to make it overspend.

The naive checker

Five worker processes behind a toy proxy; a checker that GETs /healthz every 2 s and evicts after 3 failures. Then make one worker slow (sleep 900 ms) rather than dead.

Done when: the slow-but-working worker gets evicted while serving successfully.  Teaches: a timeout cannot tell dead from slow; every threshold is a latency assumption.

Continuous suspicion

Replace the counter with phi accrual: track inter-arrival times of successful checks, emit phi, convict at a threshold. Port the logic from Cassandra's FailureDetector.java.

Done when: the same slow worker's phi rises and falls without conviction, and a killed worker convicts within seconds.  Teaches: why phi 8 is a different kind of number from "3 failures".

Passive signals

Feed real request outcomes into the same suspicion score (consecutive errors, success-rate deviation), Envoy-outlier style. Compare detection latency against active checks alone.

Done when: a worker returning 500s is caught before its next active check would have run.  Teaches: passive detection is faster and free, and blind at zero traffic.

The budget

Add the gate: at most one eviction per 30 s, at most 40% of the pool evicted, fail open past that. Now block the shared dependency all five workers check, and watch.

Done when: the correlated failure evicts one worker, then the gate locks and traffic keeps flowing; without the gate, the same event empties the pool.  Teaches: the single design difference between Slack's January 2021 and an empty backend list.

Self-report and lame duck

Give workers a NOT_SERVING state and a drain endpoint; deploy by draining before stopping. Measure request errors during a rolling restart, before and after.

Done when: a full rolling restart completes with zero failed requests.  Teaches: planned exits should be announced, not detected; the SRE book's 1–2 RTT claim, reproduced.

Hurt the checker

The Lifeguard scenario: CPU-starve the checker process itself (or add 500 ms of jitter to its network) and count false convictions over an hour, with and without scaling its own timeouts by a self-health score.

Done when: self-suspicion cuts false convictions by an order of magnitude, and you can say which component of your production stack plays the checker role and what happens when it is the sick one.  Teaches: the AWS October 2025 mechanism, at desk scale.

08

Keep hunting

The queries that actually found this material, grouped by what they surface. The vocabulary is the value: each term below was learned from one source and unlocked the next.

Incidents where the check was the cause

  • "health check" postmortem "removed capacity" OR "removed from DNS"
  • "panic mode" OR "fail open" health check outage postmortem
  • "liveness probe" cascading restart production "we"
  • "velocity control" NLB health check AZ failover

The design arguments in the open

  • site:github.com issue "livenessProbe" mass failures PodDisruptionBudget
  • site:github.com envoy outlier detection panic threshold issue
  • kubernetes KEP startupProbe "alternatives" liveness holdoff
  • grpc proposal "client-side health checking" A17

The mechanism literature

  • "phi accrual failure detector" convict threshold cassandra
  • "gray failure" "differential observability" azure
  • lifeguard swim "local health" false positives arxiv
  • SWIM "infection-style" membership suspicion DSN 2002

Defaults and budgets in source trees

  • max_ejection_percent default site:github.com envoy proto
  • renewalPercentThreshold eureka self preservation
  • phi_convict_threshold cassandra.yaml default
  • "healthy_panic_threshold" envoy runtime default 50
09

References

  1. AWS, Summary of the Amazon DynamoDB Service Disruption in US-EAST-1, Oct 19–20 2025aws.amazon.com, October 2025. Checked 2026-08-31 (search-verified).
  2. Slack Engineering, Slack's Outage on January 4th 2021slack.engineering, February 2021. Checked 2026-08-31 (search-verified).
  3. Slack Engineering, A Terrible, Horrible, No-Good, Very Bad Day at Slackslack.engineering, 2020. Checked 2026-08-31 (search-verified).
  4. GitHub, October 21 post-incident analysisgithub.blog, October 30 2018. Checked 2026-08-31 (search-verified).
  5. Roblox, Return to Service 10/28–10/31 2021about.roblox.com, January 2022. Checked 2026-08-31 (search-verified).
  6. Google Cloud, Incident report: Service Control outage, June 12 2025status.cloud.google.com, June 2025. Checked 2026-08-31 (search-verified).
  7. David Yanacek, Implementing health checksAmazon Builders' Library, December 2019. Checked 2026-08-31 (search-verified).
  8. Google, Site Reliability Engineering, ch. 20: Load Balancing in the Datacentersre.google, 2016. Checked 2026-08-31 (search-verified).
  9. Envoy, Panic threshold (architecture overview)envoyproxy/envoy docs source, current tree. Fetched 2026-08-31.
  10. Envoy, outlier_detection.protoenvoyproxy/envoy, current tree. Fetched 2026-08-31.
  11. Envoy issue #17650, Outlier detection vs admission controlgithub.com, 2021. Checked 2026-08-31 (search-verified).
  12. Kubernetes, core/v1 types.go (Probe defaults)kubernetes/kubernetes, current tree. Fetched 2026-08-31.
  13. Kubernetes, Liveness, Readiness and Startup Probeskubernetes.io; source markdown fetched from kubernetes/website 2026-08-31.
  14. Kubernetes issue #66230, Prevent mass livenessProbe failures from taking down all podsgithub.com, July 2018, lifecycle/frozen. Checked 2026-08-31 (search-verified).
  15. Kubernetes website issue #16607, Liveness Probes: mention that they can worsen app availabilitygithub.com, 2019. Checked 2026-08-31 (search-verified).
  16. Kubernetes KEP-950, Pod-startup liveness-probe holdoff (startupProbe)kubernetes/enhancements. Fetched 2026-08-31.
  17. Kubernetes KEP-2238, Probe-level terminationGracePeriodSecondskubernetes/enhancements. Fetched 2026-08-31.
  18. Apache Cassandra, FailureDetector.javaapache/cassandra trunk. Fetched 2026-08-31.
  19. Apache Cassandra, cassandra.yaml (phi_convict_threshold)apache/cassandra trunk. Fetched 2026-08-31.
  20. HashiCorp, memberlist (awareness.go, Lifeguard implementation)github.com. Cloned 2026-08-31.
  21. Netflix, Eureka (self-preservation: AbstractInstanceRegistry, DefaultEurekaServerConfig)github.com. Cloned 2026-08-31.
  22. gRPC, A17: Client-Side Health Checking (Mark D. Roth)grpc/proposal, August 2018. Fetched 2026-08-31.
  23. gRPC, Health Checking Protocolgrpc/grpc. Fetched 2026-08-31.
  24. Huang, Guo, Zhou, Lorch, Dang, Chintalapati, Yao, Gray Failure: The Achilles' Heel of Cloud-Scale SystemsHotOS 2017, Microsoft Research. Checked 2026-08-31 (search-verified).
  25. Huang and Li, Gray Failure (SREcon24 Americas talk and slides)USENIX, March 20 2024. Checked 2026-08-31 (search-verified).
  26. Das, Gupta, Motivala, SWIM: Scalable Weakly-consistent Infection-style Process Group Membership ProtocolDSN 2002, Cornell. Checked 2026-08-31 (search-verified).
  27. Hayashibara, Défago, Yared, Katayama, The phi accrual failure detectorSRDS 2004. Checked 2026-08-31 (search-verified).
  28. Dadgar, Phillips, Currey, Lifeguard: Local Health Awareness for More Accurate Failure DetectionarXiv:1707.00788, July 2017. Checked 2026-08-31 (search-verified).
  29. Colin Breck, Kubernetes Liveness and Readiness Probes: How to Avoid Shooting Yourself in the Footblog.colinbreck.com, 2019. Checked 2026-08-31 (search-verified).
  30. Henning Jacobs, Liveness Probes are Dangeroussrcco.de, 2019. Checked 2026-08-31 (search-verified).
  31. Encore, Distributed Systems Horror Stories: Kubernetes Deep Health Checksencore.dev, 2023. Checked 2026-08-31 (search-verified).
  32. Michal Drozd, Envoy Outlier Detection Brownouts: When the Mesh Ejects Healthy Podsmichal-drozd.com. Checked 2026-08-31 (search-verified).
  33. HeyOnCall, Kubernetes Liveness Probes and CPU Limits: Self-Reinforcing CrashLoopBackOffheyoncall.com. Checked 2026-08-31 (search-verified).
  34. Lorin Hochstein, Quick thoughts on the recent AWS outagesurfingcomplexity.blog, October 25 2025. Checked 2026-08-31 (search-verified).
  35. ThousandEyes, AWS Outage Analysis: October 20, 2025thousandeyes.com, October 2025. Checked 2026-08-31 (search-verified).
  36. The Downtime Project, GitHub's 43 Second Network Partitiondowntimeproject.com, 2021. Checked 2026-08-31 (search-verified).