The Deployment is the most-used workload resource in Kubernetes. It manages ReplicaSets to provide declarative rolling updates, rollbacks, and version history — the features you need to ship code to production with zero downtime.
1. Deployment Anatomy
apiVersion: apps/v1
kind: Deployment
metadata:
name: web
spec:
replicas: 3
selector:
matchLabels:
app: web
strategy: # ← how to roll out changes
type: RollingUpdate
rollingUpdate:
maxSurge: 1 # max extra Pods during update
maxUnavailable: 0 # max Pods that can be down
revisionHistoryLimit: 10 # ← how many old RSs to keep
progressDeadlineSeconds: 600 # ← how long before marking failed
template:
metadata:
labels:
app: web
spec:
containers:
- name: nginx
image: nginx:1.25
ports:
- containerPort: 80
readinessProbe:
httpGet:
path: /
port: 80
initialDelaySeconds: 5
periodSeconds: 5
Key Fields
| Field | Purpose | Default |
|---|---|---|
strategy.type | RollingUpdate or Recreate | RollingUpdate |
maxSurge | Extra Pods allowed above desired during rollout | 25% |
maxUnavailable | Pods that can be unavailable during rollout | 25% |
revisionHistoryLimit | Old ReplicaSets retained (for rollback) | 10 |
progressDeadlineSeconds | Time before rollout is considered stuck | 600 (10 min) |
minReadySeconds | Time a new Pod must be Ready before it's considered Available | 0 |
2. Rolling Updates — The Math
Rolling updates are controlled by two parameters that define how much disruption is allowed:
maxSurge and maxUnavailable
| Parameter | Meaning | Can Be |
|---|---|---|
maxSurge | Max Pods above desired count at any time | Absolute number or percentage |
maxUnavailable | Max Pods below desired count at any time | Absolute number or percentage |
Percentages are rounded up for maxSurge and rounded down for maxUnavailable.
Example: 10 replicas, maxSurge=2, maxUnavailable=1
Desired: 10 Pods Max total Pods: 10 + 2 = 12 (can have up to 12 running) Min available: 10 - 1 = 9 (at least 9 must be Ready) Step 1: Scale new RS to 2 (total: 12 = 10 old + 2 new) ✓ ≤12 Step 2: Scale old RS to 9 (total: 11 = 9 old + 2 new) ✓ available ≥9 Step 3: New Pods ready → scale new to 4, old to 8... ... continues until old=0, new=10
Common Strategy Configurations
| Configuration | Behavior | Use Case |
|---|---|---|
maxSurge=1, maxUnavailable=0 | Add one new, wait until Ready, then remove one old | Zero-downtime (safest, slowest) |
maxSurge=0, maxUnavailable=1 | Remove one old, wait for new to be Ready | No extra resources (risks brief capacity loss) |
maxSurge=25%, maxUnavailable=25% | Fast parallel update (default) | General purpose |
maxSurge=100%, maxUnavailable=0 | Blue-green style — spin up all new, then drain old | When you need instant rollback capability |
minReadySeconds have elapsed). Without a readiness probe, a Pod is "Ready" immediately after starting — meaning the rollout might proceed even if the app isn't actually serving traffic.
Recreate Strategy
The alternative to RollingUpdate: kill all old Pods first, then create all new Pods. Simple but causes downtime.
strategy: type: Recreate # All old Pods terminated → all new Pods created # Use case: app can't run two versions simultaneously (DB schema lock)
Recreate only when your app cannot have two versions running at the same time (e.g., it holds an exclusive lock, or old/new versions have incompatible DB schemas). For everything else, use RollingUpdate.
3. Rollbacks & Revision History
Each time you change the Pod template, the Deployment creates a new ReplicaSet (a new revision). Old ReplicaSets are kept (scaled to 0) for rollback.
Viewing History
# See all revisions: kubectl rollout history deployment/web # REVISION CHANGE-CAUSE # 1 kubectl apply --filename=deploy.yaml # 2 kubectl set image deployment/web nginx=nginx:1.25 # 3 kubectl set image deployment/web nginx=nginx:1.26 # See details of a specific revision: kubectl rollout history deployment/web --revision=2 # Shows the Pod template at that revision
Rolling Back
# Undo last change (rollback to revision N-1): kubectl rollout undo deployment/web # Rollback to specific revision: kubectl rollout undo deployment/web --to-revision=1 # What happens internally: # 1. Old RS (from target revision) is scaled up # 2. Current RS is scaled down # 3. The rollback becomes a NEW revision (appended, not overwritten)
Setting CHANGE-CAUSE
# Record why a change was made (shows in rollout history): kubectl annotate deployment/web kubernetes.io/change-cause="Upgrade nginx to 1.26" # Or use --record (deprecated but still works): kubectl set image deployment/web nginx=nginx:1.26 --record
revisionHistoryLimit
revisionHistoryLimit: 10 (default) means Kubernetes keeps 10 old ReplicaSets (scaled to 0). Older ones are garbage-collected. Set to 0 to disable rollback entirely (saves etcd space in large clusters).
kubectl rollout undo. The Deployment's revision history is still useful for quick incident response (undo in 2 seconds vs waiting for CI/CD), but the source of truth should always be Git.
4. Monitoring Rollouts
kubectl rollout status
# Watch a rollout in real-time: kubectl rollout status deployment/web # Waiting for deployment "web" rollout to finish: # 2 of 3 updated replicas are available... # deployment "web" successfully rolled out # Exit code: 0 = success, non-zero = timeout/failure # Useful in CI/CD pipelines to gate the next step
Deployment Conditions
kubectl get deployment web -o yaml | grep -A10 conditions: # conditions: # - type: Available # status: "True" # reason: MinimumReplicasAvailable # - type: Progressing # status: "True" # reason: NewReplicaSetAvailable
| Condition | Status: True means | Status: False means |
|---|---|---|
Available | At least minAvailable Pods are Ready | Not enough Pods Ready |
Progressing | Rollout is making progress or complete | Stuck beyond progressDeadlineSeconds |
Stuck Rollouts
If a rollout makes no progress for progressDeadlineSeconds (default 600s), the condition Progressing becomes False with reason ProgressDeadlineExceeded. This does NOT auto-rollback — it's just a signal.
# Common causes of stuck rollouts: # - New image doesn't exist (ImagePullBackOff) # - Readiness probe never passes (app crashes on startup) # - Insufficient resources (Pod stuck Pending) # - Quota exceeded # Fix: undo the rollout kubectl rollout undo deployment/web
Pausing and Resuming
# Pause — make multiple changes without triggering a rollout for each: kubectl rollout pause deployment/web kubectl set image deployment/web nginx=nginx:1.26 kubectl set resources deployment/web -c nginx --limits=memory=256Mi kubectl rollout resume deployment/web # Single rollout triggered with both changes
kubectl rollout pause/resume is tested in CKA. Know that while paused, changes accumulate but no rollout happens. Resuming triggers one consolidated rollout. You can't rollback while paused.
5. Production Patterns
Triggering Rollouts Without Code Changes
Sometimes you need to restart all Pods (e.g., to pick up a rotated Secret):
# Restart all Pods (creates new RS with same template + annotation change): kubectl rollout restart deployment/web # Under the hood: adds annotation # kubectl.kubernetes.io/restartedAt: "2024-01-15T10:30:00Z" # This changes the template → triggers rolling update
Canary Deployments (Manual)
# Approach: pause rollout after partial scale-up kubectl set image deployment/web nginx=nginx:1.26 # Immediately pause when a few new Pods are running: kubectl rollout pause deployment/web # Monitor canary Pods, check metrics/logs # If good: resume kubectl rollout resume deployment/web # If bad: undo kubectl rollout undo deployment/web
Blue-Green with Deployments
# Strategy: maxSurge=100%, maxUnavailable=0
# Effect: all new Pods come up BEFORE any old Pods are removed
strategy:
type: RollingUpdate
rollingUpdate:
maxSurge: "100%" # double the Pods temporarily
maxUnavailable: 0 # zero downtime guaranteed
# Total Pods during rollout: up to 2x desired
# Requires double the node capacity temporarily
Config Change Triggers
Changing a ConfigMap doesn't trigger a Deployment rollout (Pods reference ConfigMaps, but the Pod template doesn't change). Common pattern to force a rollout on config change:
# Add a hash of the config as an annotation:
spec:
template:
metadata:
annotations:
configHash: "sha256:abc123..." # computed by CI/CD
# When ConfigMap changes → hash changes → template changes → rollout triggered
6. Deployment vs Directly Managing Pods
| Feature | Bare Pod | ReplicaSet | Deployment |
|---|---|---|---|
| Self-healing | ❌ | ✅ | ✅ |
| Scaling | ❌ | ✅ | ✅ |
| Rolling updates | ❌ | ❌ | ✅ |
| Rollback | ❌ | ❌ | ✅ |
| Revision history | ❌ | ❌ | ✅ |
| Pause/resume | ❌ | ❌ | ✅ |
Summary
| Concept | Key Point |
|---|---|
| Rolling update | New RS scales up, old RS scales down — controlled by maxSurge/maxUnavailable |
| maxSurge | How many extra Pods above desired (controls speed vs resource cost) |
| maxUnavailable | How many Pods below desired (controls acceptable downtime) |
| Revision | Each template change = new revision (old RS kept at scale 0) |
| Rollback | rollout undo — creates new revision with old template |
| Readiness probe | Essential — without it, rollout proceeds even if new Pods are broken |
| progressDeadlineSeconds | How long to wait before marking rollout as stuck |
📝 Quiz: Deployments
Q1: A Deployment has 4 replicas, maxSurge=50%, maxUnavailable=25%. What's the max and min number of Pods during rollout?
Min available: 3 Pods (4 - floor(25% × 4) = 4 - 1 = 3).
So during rollout, between 3 and 6 Pods will exist at any time.
Q2: You set maxSurge=0, maxUnavailable=0. What happens?
Q3: A new Pod's readiness probe fails for 10 minutes. The rollout has maxSurge=1, maxUnavailable=0. What happens?
progressDeadlineSeconds (default 600s), the Progressing condition becomes False. No auto-rollback — you must intervene.Q4: You roll back from revision 5 to revision 3. What revision number does the rollback create?
Q5: You update a ConfigMap that a Deployment's Pods reference. Do the Pods restart?
kubectl rollout restart or add a config hash annotation to the Pod template.Q6: What's the advantage of maxSurge=1, maxUnavailable=0 vs the default 25%/25%?