Updating servers a few at a time
Open in the interactive app: practice, quiz, mock interview →In one minute
- A rolling deployment replaces instances of the old version with the new one in small batches, so the service never fully goes down and never needs double capacity.
- Two knobs control it: how many extra instances you may add (surge) and how many may be unavailable at once. Together they set speed versus spare capacity.
- Readiness checks decide when a new instance takes traffic and when the next batch may start. Bad checks make rolling deploys unsafe.
- During the rollout both versions serve traffic at the same time, so every change must be backward and forward compatible.
- Rollback is just another rolling deploy in reverse, which takes as long as the rollout did.
How it works
The loop
The orchestrator starts new-version instances, waits for readiness, adds them to the load balancer, then drains and terminates old instances, repeating until none remain. Kubernetes expresses this as maxSurge (extra pods above desired) and maxUnavailable (pods below desired that may be not ready). With 20 replicas, maxSurge=5 and maxUnavailable=0, it runs up to 25 pods and always keeps 20 ready. Outside Kubernetes the same idea holds: Amazon's pipelines typically replace at most a third of a fleet at once, keeping at least two thirds healthy, and some teams go as low as 5% (AWS Builders' Library).
Readiness versus liveness
Readiness means "send me traffic". Liveness means "restart me". A readiness probe should check that the process has loaded config, warmed critical caches, and can reach hard dependencies. If readiness returns true too early, the rollout races ahead and users hit cold instances. Add a minimum ready time (for example 30 seconds) so a pod that crashes 10 seconds after start does not count as healthy; Kubernetes' minReadySeconds defaults to 0, so this is opt-in (Kubernetes docs). Load balancers can help too: an AWS target group's slow start ramps a new target's share of traffic linearly over 30 to 900 seconds instead of giving it a full share at once (AWS docs).
Draining
Terminating an old instance must be graceful. The orchestrator removes it from the load balancer, the instance stops accepting new connections, finishes in-flight requests within a grace period, then exits. Endpoint removal propagates asynchronously, so a short pre-stop delay of a few seconds avoids sending requests to a process that has already closed its listener. Kubernetes now has a built-in sleep action for preStop hooks, stable since v1.34, so images no longer need a sleep binary just for this (KEP-3960). The grace period clock starts before the hook runs, so the hook and the app's own shutdown must both fit inside terminationGracePeriodSeconds (Kubernetes docs).
Progress and halt
A rollout needs a deadline. If new instances do not become ready within, say, 10 minutes, the controller marks the rollout failed and stops creating more. It does not roll back on its own unless your tooling adds that. Better pipelines watch error rates and latency on new instances during the rollout and trigger an automated rollback.
Mixed versions
For the whole rollout window, some requests hit v1 and some hit v2. A user can bounce between them on consecutive requests. Anything shared, such as the database schema, cache entries, message formats, and session data, must be readable by both versions.
Down the stack
Layer 1The contract
In Kubernetes, the Deployment controller promises two bounds throughout an update: available pods never fall below replicas - maxUnavailable, and pods counted toward the rollout never exceed replicas + maxSurge. Terminating pods are not counted, so while old pods drain the real pod count and resource use can exceed replicas + maxSurge until their grace period ends; since v1.35 (beta) the Deployment reports them in .status.terminatingReplicas (Kubernetes docs). Percentages round surge up and unavailability down. "Available" means ready for at least minReadySeconds. If no progress happens within progressDeadlineSeconds (600 by default), the Deployment reports Progressing=False with reason ProgressDeadlineExceeded. It does not roll back on its own. The contract says nothing about request success: dropping in-flight requests is your problem.
Layer 2The mechanism
A Deployment owns ReplicaSets, one per pod template, named with a pod-template-hash label. An update creates a new ReplicaSet and the controller's reconcile loop scales it up and the old one down in steps that respect the two bounds. Old ReplicaSets are kept at zero replicas (revisionHistoryLimit, default 10), so rollout undo simply scales an old one back up; setting the limit to 0 deletes that history and with it the ability to roll back (Kubernetes docs). When a pod becomes ready, the EndpointSlice controller adds its IP; when it starts terminating, the IP is removed. Termination runs the preStop hook, then sends SIGTERM, then SIGKILL after terminationGracePeriodSeconds (30 by default).
Layer 3The machine
Endpoint removal is not instantaneous. The change goes from the API server through watches to kube-proxy on every node, which rewrites its packet-forwarding rules: iptables is still the default mode in Kubernetes 1.37, with IPVS and nftables as alternatives and nftables planned as the future default (Kubernetes docs). On a 1,000-node cluster with many services, this can take several seconds, and existing TCP connections keep flowing through conntrack entries to the old pod IP. Clients using HTTP keep-alive stay pinned to the old pod until the connection closes, which is why servers send Connection: close or an HTTP/2 GOAWAY during drain. A new pod pays for image pull (hundreds of MB at roughly 100 MB/s if layers are not cached on the node), process start, JIT warmup on the JVM, empty CPU caches, and new TLS handshakes to every dependency for its connection pools. Its first requests can be several times slower than a warm pod's.
Why it's built this way
Controllers reconcile desired state in loops, so a rollout survives controller restarts and is resumable. Pod templates are immutable, so each version gets its own ReplicaSet, and rollback is just scaling. Endpoint state spreads to many nodes asynchronously, so pods need a pre-stop delay to avoid receiving traffic after shutdown. Nodes are shared and bin-packed, so surge capacity is limited by free cluster space, which is why both knobs exist.
Diagram
sequenceDiagram participant O as Orchestrator participant N as New pod v2 participant LB as Load balancer participant Old as Old pod v1 O->>N: create (surge slot) N-->>O: readiness OK after warmup O->>LB: add v2 endpoint O->>LB: remove v1 endpoint O->>Old: SIGTERM after preStop delay Old-->>Old: finish in-flight requests Old-->>O: exited O->>O: next batch until v1 count is 0
Worked example
A checkout API runs 40 pods at 60 percent CPU at peak, each handling 250 RPS, 10,000 RPS total. Pods take 45 seconds to start and become ready.
With maxUnavailable=10% and maxSurge=0, the deploy removes 4 pods at a time. The other 36 must absorb 10,000 RPS, about 278 RPS each, roughly 67 percent CPU. That is safe. The rollout needs 10 batches of about 60 seconds each including drain, so about 10 minutes.
With maxUnavailable=25%, 30 pods carry 333 RPS each at 80 percent CPU. Latency climbs and autoscaling may fire mid-deploy, adding noise. Faster, but riskier at peak.
With maxSurge=25% and maxUnavailable=0, the cluster briefly runs 50 pods. No capacity loss, and 4 batches finish in about 4 minutes, but the cluster needs room for 10 extra pods. If the node pool is full, the surge pods stay pending and the rollout stalls. The usual choice is surge-based rolling with spare headroom reserved, or a smaller unavailability budget off-peak.
In the wild
- Knight Capital, 2012: a mixed fleet with incompatible meaning. New order-routing code reached seven of eight servers. The new release reused a flag that, on the eighth server's old code, switched on long-retired "Power Peg" logic, so the two versions read the same signal differently. Removing the new code from the other seven made it worse, because they then ran the old path too. Knight lost more than $460 million in about 45 minutes (SEC order).
- Slack, 2022: a gradual agent rollout that emptied the cache. A Consul agent upgrade rolled through a quarter of the fleet, restarting agents one after another. Each restart briefly deregistered memcached nodes, which were replaced with empty ones, and every miss turned into a query across all database shards. The database overloaded, the cache could not refill, and many users could not load Slack. Slack changed how Consul rollouts proceed and re-sharded the hot table (Slack).
- Discord, 2026: pods terminated before they could hand off. A config change made session pods larger and halved their number in one zone. Pods were killed before draining, so 17% of sessions ended ungracefully, and the reconnect storm overran rate limits and gateway memory. Voice and video were badly degraded for about 3h17m. The fix included an admission webhook that blocks scale-down until pods drain (Discord).
Trade-offs
| Option | Strengths | Weaknesses | Choose when |
|---|---|---|---|
| Rolling with surge | No capacity loss, no full duplicate fleet | Needs spare cluster headroom, mixed versions | Default for stateless services |
| Rolling with unavailability | No extra capacity needed | Reduced capacity during deploy | Tight clusters, off-peak deploys |
| Recreate (stop all, start all) | No version mixing | Full downtime | Incompatible changes, dev environments |
| Blue-green | Instant switch and instant rollback | Double capacity, still mixing at the data layer | Rollback speed matters more than cost |
Pitfalls in production
- Readiness that only checks "process is up" lets the rollout proceed before caches warm. Make readiness reflect real ability to serve and add a minimum ready time.
- A rollout that stalls halfway leaves a mixed fleet for hours. Set a progress deadline and alert on it.
- Long-lived connections (WebSockets, gRPC streams) do not move when a pod is removed from the balancer. Send a goaway or close message and let clients reconnect with jitter.
- Rolling deploys of stateful sets reorder quorum. Roll one replica at a time and wait for it to rejoin and catch up before the next.
- Rollback of 200 pods takes as long as the rollout. If you need seconds, not minutes, use blue-green or flags.
Leadership lens
- Ask what the capacity budget is during a deploy and whether it holds at peak traffic.
- Require every change to state its compatibility story: can v1 and v2 run together against the same data?
- Decide whether rollouts gate on metrics automatically. A rolling deploy watched by nobody is a slow outage.
- Keep deploy duration visible. A 40-minute rollout discourages small, frequent releases.
Check yourself
With 10 replicas,
maxSurge=0andmaxUnavailable=0, what happens?Show answer
The rollout cannot make progress: it may not add a pod and may not remove one. Kubernetes rejects this combination for that reason.
Why can a readiness probe that returns 200 as soon as the HTTP server binds make a rolling deploy dangerous?
Show answer
Old pods are removed as soon as new ones report ready, so traffic shifts to instances that have not loaded config or warmed caches, causing errors or latency spikes across the whole fleet.
Why is rollback with a rolling strategy slower than with blue-green?
Show answer
Rolling back replaces instances batch by batch with the same pacing as the rollout, while blue-green keeps the old fleet running and just switches traffic back.
A v2 writes a new enum value to a shared table during the rollout. What breaks?
Show answer
v1 instances still serving traffic may read the unknown value and fail. The value should only be written after every instance can read it.
Sources and further study
Primary
- KEP-3960: Pod lifecycle sleep action: the design and graduation history of the built-in
preStopsleep. - Slack: incident on 2-22-22: a rolling restart of infrastructure agents that cascaded into a database overload.
Systems docs
- Kubernetes: container lifecycle hooks:
preStophandlers and how they share the termination grace period. - Kubernetes: virtual IPs and service proxies: kube-proxy modes and why endpoint changes take time to land on every node.
- AWS: target groups for Application Load Balancers: deregistration delay (300 seconds by default) and slow start.
Books
- Release It! (2nd edition), Michael T. Nygard, chapter 13 "Design for Deployment": automated deployments and the phases of a rolling change.
- Site Reliability Engineering, Beyer et al., chapter 8 "Release Engineering": staged rollouts as part of release automation.
- Understanding Distributed Systems (2nd edition), Roberto Vitillo, chapter "Continuous delivery and deployment": incremental rollouts, health gates and rollback.
Other curricula
- Azure Well-Architected: safe deployment practices: treats any gradual replacement as progressive exposure, gated by a health model and bake time.
- donnemartin/system-design-primer: does not cover rolling updates; deployment appears only as a cost of microservices.
Watch
- Top 5 Most-Used Deployment Strategies (ByteByteGo): places rolling updates next to the alternatives.
- Kubernetes Deployment Patterns: rolling updates, canary and blue/green (Appsbroker Academy): demonstrates the Kubernetes rolling update knobs.
Free resources
- Kubernetes Deployments: authoritative reference for maxSurge, maxUnavailable, progress deadlines, and rollout history.
- Liveness, Readiness, and Startup Probes: how each probe affects traffic and restarts.
- Ensuring rollback safety during deployments (AWS Builders' Library): the two-phase approach to changes that must coexist with the previous version.
- Pod disruptions: disruption budgets and how voluntary evictions interact with rollouts.