Skip to main content
RunBook Academy

KubernetesXV · DeploymentsDeployments

Rolling updates — maxSurge, maxUnavailable, and rollout phases

Advanced⏱ ~16 minkubectl

What you'll learn

  • Trace the rollout phases (scaling new RS up, scaling old RS down)
  • Configure maxSurge and maxUnavailable for the workload
  • Reason about the trade-offs (capacity vs availability)
  • Diagnose stuck rollouts

Prerequisites

Verified against Kubernetes 1.34.x · kubeadm 1.34.x · kubectl 1.34.x · etcd 3.6.x · CoreDNS 1.11.x · containerd 1.7.x / 2.x · 2026-08-16

Not yet marked complete on this device.

Rolling updates are the standard Deployments rollout strategy. This lesson explains the rollout phases, the meaning of maxSurge and maxUnavailable, and how to tune them for the workload.

The rollout phases

sequenceDiagram
    participant D as Deployment
    participant New as New ReplicaSet
    participant Old as Old ReplicaSet

    Note over D,Old: Initial: Old RS = 3 Pods
    D->>New: create new RS (0 Pods)
    D->>New: scale to 1 (maxSurge allows)
    D->>Old: scale to 2 (maxUnavailable allows)
    D->>New: scale to 2
    D->>Old: scale to 1
    D->>New: scale to 3
    D->>Old: scale to 0
    Note over D,Old: Final: New RS = 3 Pods

The phases:

  1. Create new ReplicaSet: the Deployment creates the new RS at 0 replicas.
  2. Scale up new: incrementally scale up the new RS, respecting maxSurge.
  3. Scale down old: incrementally scale down the old RS, respecting maxUnavailable.
  4. Final state: new RS at desired replicas; old RS at 0 (but retained for revision history).

The total number of Pods oscillates between replicas - maxUnavailable and replicas + maxSurge during the rollout.

maxSurge and maxUnavailable

strategy:
  type: RollingUpdate
  rollingUpdate:
    maxSurge: 25%
    maxUnavailable: 25%

Both can be a number or a percentage of replicas.

maxSurge: how many Pods can exist above the desired replicas during the rollout.

  • maxSurge: 25% of 3 replicas = max surge of 1 Pod.
  • maxSurge: 0: no surge allowed (every new Pod requires an old Pod to be removed first).
  • maxSurge: 100%: double the replicas during the rollout (every old Pod has a replacement before removal).

maxUnavailable: how many Pods can be unavailable during the rollout.

  • maxUnavailable: 25% of 3 replicas = max unavailable of 1.
  • maxUnavailable: 0: no unavailable allowed (every old Pod must have a replacement before being removed).
  • maxUnavailable: 100%: all old Pods can be removed at once (the cluster temporarily serves fewer Pods).

Tuning maxSurge and maxUnavailable

The trade-offs:

SettingCapacity impactAvailability impact
maxSurge: 25%, maxUnavailable: 25%Up to 25% extra capacity during rolloutUp to 25% unavailable
maxSurge: 100%, maxUnavailable: 0%Up to 100% extra capacityZero unavailability
maxSurge: 0%, maxUnavailable: 100%No extra capacityUp to 100% unavailable
maxSurge: 25%, maxUnavailable: 0%Up to 25% extra capacityZero unavailability

For a service that must not lose capacity during rollouts:

strategy:
  type: RollingUpdate
  rollingUpdate:
    maxSurge: 25%       # extra capacity during rollout
    maxUnavailable: 0%   # never fewer Pods than desired

This requires the cluster to have spare capacity for the surge.

For a service that can tolerate temporary unavailability:

strategy:
  type: RollingUpdate
  rollingUpdate:
    maxSurge: 0%
    maxUnavailable: 25%

No extra capacity needed; some Pods are unavailable during the rollout.

How the rollout proceeds

For 3 replicas, maxSurge: 25%, maxUnavailable: 25%:

gantt
    title Rolling update (3 replicas, maxSurge 25%, maxUnavailable 25%)
    dateFormat X
    axisFormat %s
    section Old RS
    Old RS :a1, 0, 5s
    Old RS :a2, 5, 5s
    Old RS :a3, 10, 5s
    Old RS :a4, 15, 5s
    Old RS :a5, 20, 5s
    Old RS :a6, 25, 5s
    Old RS :a7, 30, 5s
    Old RS :a8, 35, 5s
    Old RS :a9, 40, 5s
    section New RS
    New RS :b1, 0, 5s
    New RS :b2, 5, 10s
    New RS :b3, 15, 5s
    New RS :b4, 25, 5s
    New RS :b5, 35, 5s
    New RS :b6, 45, 5s

At each step:

  • Step 1: Old=3, New=0, Total=3. (Old=3)
  • Step 2: Old=3, New=1, Total=4. (Surge 1)
  • Step 3: Old=2, New=1, Total=3. (Unavailable 1)
  • Step 4: Old=2, New=2, Total=4. (Surge 1)
  • Step 5: Old=1, New=2, Total=3. (Unavailable 1)
  • Step 6: Old=1, New=3, Total=4. (Surge 1)
  • Step 7: Old=0, New=3, Total=3. (Unavailable 1)

The total oscillates between 3 and 4; old count decreases while new increases.

Why Deployments pause rollouts

During the rollout, the Deployment waits for the new Pods to become Ready before scaling down old Pods further. If the new Pod fails readiness, the rollout stalls.

flowchart LR
    Create[Create new Pod] --> Ready{Ready?}
    Ready -- yes --> Next[Scale down old]
    Ready -- no --> Wait[Wait for Ready]
    Wait --> Timeout{Reached<br/>progress deadline?}
    Timeout -- no --> Ready
    Timeout -- yes --> Fail[Mark rollout failed]

The progressDeadlineSeconds (default 600s) controls how long the Deployment waits before marking the rollout as failed.

Production discipline: set progressDeadlineSeconds deliberately. A short deadline (60s) fails fast; a long deadline (1800s) tolerates slow rollouts.

Diagnosing stuck rollouts

A rollout that won’t progress:

kubectl rollout status deployment/web --timeout=60s
# error: deployment "web" exceeded its progress deadline

Common causes:

  • New Pod fails readiness probe: check the readiness probe configuration.
  • New Pod cannot start: ImagePullBackOff, runtime error.
  • Insufficient capacity: cluster cannot fit the surge.
  • PDB blocking: PodDisruptionBudget prevents scaling down old Pods.
# Check Pod status
kubectl get pods -l app=web

# Check events
kubectl describe deployment/web | tail -30

# Check PDB
kubectl get pdb -n team-a-prod

Production patterns

High-availability HTTP service:

strategy:
  type: RollingUpdate
  rollingUpdate:
    maxSurge: 25%
    maxUnavailable: 0%       # never lose capacity

maxUnavailable: 0% ensures the cluster always has the desired number of Pods. The trade-off: cluster needs surge capacity.

Resource-constrained batch worker:

strategy:
  type: RollingUpdate
  rollingUpdate:
    maxSurge: 0%
    maxUnavailable: 50%

maxSurge: 0% means no extra capacity; maxUnavailable: 50% means up to half the Pods can be down during rollout. Suitable for batch workers where temporary unavailability is tolerable.

Fast rollout for low-replica services:

spec:
  replicas: 2
strategy:
  type: RollingUpdate
  rollingUpdate:
    maxSurge: 1
    maxUnavailable: 0

With 2 replicas and maxSurge: 1, maxUnavailable: 0, the rollout adds 1 new Pod, waits for it to be Ready, then removes 1 old Pod.

Cross-course references

  • The Linux course part XXXVII-Linux-Resources covers process restart patterns; rolling updates are the cluster-level equivalent.
  • The Ansible course part XXXV-Ansible-Scripting covers rolling restarts; kubectl rollout is the cluster-level equivalent.
  • The Docker course part XXXIV-Docker-Production covers blue-green deploys; rolling updates are a simpler alternative.

Quiz

Knowledge check · 4 questions

  1. Q1. A Deployment has `maxSurge: 0%` and `maxUnavailable: 0%`. What happens?

  2. Q2. `maxSurge: 25%` requires the cluster to have 25% extra capacity during the rollout.

  3. Q3. A Deployment's rollout stalls at `Progressing=False`. New Pods are in `Pending`. The cluster has spare capacity on most nodes but the surge Pod is pending on a specific node pool. Diagnose.

    Deployment `web` with 5 replicas, `maxSurge: 25%, maxUnavailable: 25%`. Rollout started; 1 new Pod created; the rollout stalled. The new Pod is Pending with `nodeAffinity` not satisfied. The cluster has 10 nodes, 8 with spare capacity. 2 nodes have no spare capacity.

  4. Q4. When is `maxSurge: 0%, maxUnavailable: 25%` the right choice for a Deployment?

Passing score: 75%. Answers are checked in this browser.

Production discipline

  • Configure maxSurge and maxUnavailable deliberately. Default is 25% / 25%; tune based on the workload’s tolerance.
  • Ensure surge capacity before rolling out. Monitor cluster utilisation; pre-scale if needed.
  • Avoid maxSurge: 0%, maxUnavailable: 0%. This deadlocks the rollout.
  • Set progressDeadlineSeconds deliberately. Default 600s; tune based on the rollout’s expected duration.
  • Test rollouts in staging first. Verify the maxSurge/maxUnavailable settings match the cluster’s capacity.