Kubernetes · lesson 3 of 10
Intermediate
6Advanced
1Also part of Kubernetes
5- + Deploying FastAPI with Docker and Kubernetes: Images, Signals, and Probes from FastAPI
- + Deployment Strategies Compared: Rolling, Blue-Green, and Canary from DevOps
- + GitOps in Practice: Git as the Source of Truth from DevOps
- + Kubernetes Pod Security Admission: Enforcing the Restricted Profile from Cloud Security
- + CIS Kubernetes Benchmark Checks with kube-bench in kind from Cloud Security
Also part of: DevOps
Deployments and Rollouts
Learn how Deployments manage replica sets, rolling updates, rollbacks, and rollout strategies in Kubernetes.
By SkillByExample · Updated
What is a Deployment?
A Deployment provides declarative updates for stateless applications. You describe the desired state and Kubernetes drives the cluster to match it — handling replica counts, rolling updates, and rollbacks automatically.
Deployment
└── ReplicaSet (v1)
├── Pod 1
├── Pod 2
└── Pod 3
When you update the image, Kubernetes creates a new ReplicaSet and gradually shifts Pods from old to new.
Creating a Deployment
# deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: api-deployment
labels:
app: api
spec:
replicas: 3
selector:
matchLabels:
app: api # must match template labels
template:
metadata:
labels:
app: api
spec:
containers:
- name: api
image: nginx:1.25
ports:
- containerPort: 80
resources:
requests:
cpu: "100m"
memory: "64Mi"
limits:
cpu: "250m"
memory: "128Mi"
readinessProbe:
httpGet:
path: /
port: 80
initialDelaySeconds: 5
periodSeconds: 5
# Apply the deployment
kubectl apply -f deployment.yaml
# Watch pods come up
kubectl get pods --watch
# See the deployment status
kubectl get deployments
kubectl rollout status deployment/api-deployment
# Inspect the ReplicaSet it created
kubectl get replicasets
Scaling
# Scale to 5 replicas imperatively
kubectl scale deployment api-deployment --replicas=5
# Or edit the YAML and re-apply
# (change replicas: 3 → replicas: 5)
kubectl apply -f deployment.yaml
# Watch pods scale up
kubectl get pods -w
Rolling Updates
A rolling update replaces Pods gradually so your app stays available.
spec:
strategy:
type: RollingUpdate
rollingUpdate:
maxSurge: 1 # allow 1 extra pod above desired count
maxUnavailable: 0 # never have fewer than desired count
# Update the container image (triggers a rolling update)
kubectl set image deployment/api-deployment api=nginx:1.26
# Or edit the YAML, change image tag, then:
kubectl apply -f deployment.yaml
# Watch the rollout in real time
kubectl rollout status deployment/api-deployment
# See detailed rollout events
kubectl describe deployment api-deployment
Rollback
# Check rollout history
kubectl rollout history deployment/api-deployment
# See details of a specific revision
kubectl rollout history deployment/api-deployment --revision=2
# Roll back to the previous revision
kubectl rollout undo deployment/api-deployment
# Roll back to a specific revision
kubectl rollout undo deployment/api-deployment --to-revision=1
# Confirm the rollback took effect
kubectl rollout status deployment/api-deployment
kubectl get pods
Pause and Resume a Rollout
Useful when you need to make multiple changes without triggering a rollout on each one.
# Pause the rollout
kubectl rollout pause deployment/api-deployment
# Make several changes
kubectl set image deployment/api-deployment api=nginx:1.27
kubectl set resources deployment/api-deployment -c api --limits=cpu=500m,memory=256Mi
# Resume — triggers a single rollout with all changes
kubectl rollout resume deployment/api-deployment
Recreate Strategy
Suitable for apps that cannot run two versions simultaneously (e.g., schema migrations).
spec:
strategy:
type: Recreate # terminate ALL old pods first, then create new ones
Deployment vs ReplicaSet vs Pod
| Object | Use directly? | Purpose |
|---|---|---|
| Pod | Rarely | Single instance, no self-healing |
| ReplicaSet | Rarely | Maintains N replicas — managed by Deployment |
| Deployment | Yes, always | Manages rollouts + rollbacks via ReplicaSets |
# Never create ReplicaSets directly — use Deployments
# kubectl get all shows the full hierarchy
kubectl get all -l app=api
Useful Day-to-Day Commands
# Edit a live deployment in your editor
kubectl edit deployment api-deployment
# Restart all pods (triggers a rolling restart)
kubectl rollout restart deployment/api-deployment
# Delete a deployment (and all its pods)
kubectl delete deployment api-deployment
# Delete from file
kubectl delete -f deployment.yaml
Learning Outcomes
After this tutorial you can:
- Write a Deployment manifest with
replicas,selector, andstrategy - Perform rolling updates and rollbacks with
kubectl rollout - Scale deployments imperatively and declaratively
- Explain Deployment → ReplicaSet → Pod hierarchy
Practice
- Apply
deployment.yaml, scale it to 5 withkubectl scale, then runkubectl apply -f deployment.yamlagain without editing the file. How many replicas do you end up with?
Solution
kubectl apply -f deployment.yaml
kubectl rollout status deployment/api-deployment
kubectl scale deployment api-deployment --replicas=5
kubectl rollout status deployment/api-deployment
kubectl get deployment api-deployment
kubectl apply -f deployment.yaml
kubectl get deployment api-deployment
$ kubectl apply -f deployment.yaml
deployment.apps/api-deployment created
$ kubectl rollout status deployment/api-deployment
Waiting for deployment "api-deployment" rollout to finish: 0 of 3 updated replicas are available...
Waiting for deployment "api-deployment" rollout to finish: 1 of 3 updated replicas are available...
Waiting for deployment "api-deployment" rollout to finish: 2 of 3 updated replicas are available...
deployment "api-deployment" successfully rolled out
$ kubectl scale deployment api-deployment --replicas=5
deployment.apps/api-deployment scaled
$ kubectl rollout status deployment/api-deployment
Waiting for deployment "api-deployment" rollout to finish: 3 of 5 updated replicas are available...
Waiting for deployment "api-deployment" rollout to finish: 4 of 5 updated replicas are available...
deployment "api-deployment" successfully rolled out
$ kubectl get deployment api-deployment
NAME READY UP-TO-DATE AVAILABLE AGE
api-deployment 5/5 5 5 18s
$ kubectl apply -f deployment.yaml
deployment.apps/api-deployment configured
$ kubectl get deployment api-deployment
NAME READY UP-TO-DATE AVAILABLE AGE
api-deployment 3/3 3 3 18s
The file still says replicas: 3, and apply makes the cluster match the file, so the two
extra Pods are removed. Pick one source of truth: if replicas live in Git, change them there,
not with kubectl scale.
- Update the image to
nginx:1.26and list the ReplicaSets. How many are there, and what is each one’s desired count?
Solution
kubectl set image deployment/api-deployment api=nginx:1.26
kubectl rollout status deployment/api-deployment
kubectl get replicasets -l app=api
$ kubectl set image deployment/api-deployment api=nginx:1.26
deployment.apps/api-deployment image updated
$ kubectl rollout status deployment/api-deployment
Waiting for deployment "api-deployment" rollout to finish: 1 out of 3 new replicas have been updated...
Waiting for deployment "api-deployment" rollout to finish: 1 out of 3 new replicas have been updated...
Waiting for deployment "api-deployment" rollout to finish: 1 out of 3 new replicas have been updated...
Waiting for deployment "api-deployment" rollout to finish: 1 out of 3 new replicas have been updated...
Waiting for deployment "api-deployment" rollout to finish: 1 out of 3 new replicas have been updated...
Waiting for deployment "api-deployment" rollout to finish: 2 out of 3 new replicas have been updated...
Waiting for deployment "api-deployment" rollout to finish: 2 out of 3 new replicas have been updated...
Waiting for deployment "api-deployment" rollout to finish: 2 out of 3 new replicas have been updated...
Waiting for deployment "api-deployment" rollout to finish: 2 out of 3 new replicas have been updated...
Waiting for deployment "api-deployment" rollout to finish: 1 old replicas are pending termination...
Waiting for deployment "api-deployment" rollout to finish: 1 old replicas are pending termination...
Waiting for deployment "api-deployment" rollout to finish: 1 old replicas are pending termination...
deployment "api-deployment" successfully rolled out
$ kubectl get replicasets -l app=api
NAME DESIRED CURRENT READY AGE
api-deployment-5c76c47bbd 0 0 0 58s
api-deployment-6485d6b64c 3 3 3 39s
Two. The old ReplicaSet is scaled to 0 but kept, because it is the rollback target. The suffix is a hash of the Pod template, so a new image means a new hash and a new ReplicaSet.
- Roll out an image that doesn’t exist,
nginx:9.99, with a 45-second timeout onrollout status. Are the old Pods still serving? Then recover.
Solution
kubectl set image deployment/api-deployment api=nginx:9.99
kubectl rollout status deployment/api-deployment --timeout=45s
kubectl get pods -l app=api
kubectl rollout undo deployment/api-deployment
kubectl rollout status deployment/api-deployment
kubectl get replicasets -l app=api
$ kubectl set image deployment/api-deployment api=nginx:9.99
deployment.apps/api-deployment image updated
$ kubectl rollout status deployment/api-deployment --timeout=45s
Waiting for deployment "api-deployment" rollout to finish: 1 out of 3 new replicas have been updated...
Waiting for deployment "api-deployment" rollout to finish: 1 out of 3 new replicas have been updated...
error: timed out waiting for the condition
$ kubectl get pods -l app=api
NAME READY STATUS RESTARTS AGE
api-deployment-6485d6b64c-7x5vn 1/1 Running 0 54s
api-deployment-6485d6b64c-824t8 1/1 Running 0 62s
api-deployment-6485d6b64c-lk9jj 1/1 Running 0 84s
api-deployment-bfbfb4485-blgjf 0/1 ImagePullBackOff 0 45s
$ kubectl rollout undo deployment/api-deployment
deployment.apps/api-deployment rolled back
$ kubectl rollout status deployment/api-deployment
deployment "api-deployment" successfully rolled out
$ kubectl get replicasets -l app=api
NAME DESIRED CURRENT READY AGE
api-deployment-5c76c47bbd 0 0 0 104s
api-deployment-6485d6b64c 3 3 3 85s
api-deployment-bfbfb4485 0 0 0 46s
All three old Pods kept serving. The manifest sets no strategy, so the default 25% surge and
25% unavailable apply, which on 3 replicas round to 1 extra Pod and 0 unavailable. The new Pod
never became Ready, so no old Pod was removed. undo returned instantly because the
6485d6b64c Pods were still running.
- Pause the Deployment, change both the image (
nginx:1.27) and the CPU/memory limits, then resume. Userollout historybefore and after to count how many revisions that created.
Solution
kubectl rollout history deployment/api-deployment
kubectl rollout pause deployment/api-deployment
kubectl set image deployment/api-deployment api=nginx:1.27
kubectl set resources deployment/api-deployment -c api --limits=cpu=500m,memory=256Mi
kubectl get replicasets -l app=api
kubectl rollout resume deployment/api-deployment
kubectl rollout status deployment/api-deployment
kubectl rollout history deployment/api-deployment
$ kubectl rollout history deployment/api-deployment
deployment.apps/api-deployment
REVISION CHANGE-CAUSE
1 <none>
3 <none>
4 <none>
$ kubectl rollout pause deployment/api-deployment
deployment.apps/api-deployment paused
$ kubectl set image deployment/api-deployment api=nginx:1.27
deployment.apps/api-deployment image updated
$ kubectl set resources deployment/api-deployment -c api --limits=cpu=500m,memory=256Mi
deployment.apps/api-deployment resource requirements updated
$ kubectl get replicasets -l app=api
NAME DESIRED CURRENT READY AGE
api-deployment-5c76c47bbd 0 0 0 2m2s
api-deployment-6485d6b64c 3 3 3 103s
api-deployment-bfbfb4485 0 0 0 64s
$ kubectl rollout resume deployment/api-deployment
deployment.apps/api-deployment resumed
$ kubectl rollout status deployment/api-deployment
Waiting for deployment "api-deployment" rollout to finish: 1 out of 3 new replicas have been updated...
Waiting for deployment "api-deployment" rollout to finish: 1 out of 3 new replicas have been updated...
Waiting for deployment "api-deployment" rollout to finish: 1 out of 3 new replicas have been updated...
Waiting for deployment "api-deployment" rollout to finish: 2 out of 3 new replicas have been updated...
Waiting for deployment "api-deployment" rollout to finish: 2 out of 3 new replicas have been updated...
Waiting for deployment "api-deployment" rollout to finish: 2 out of 3 new replicas have been updated...
Waiting for deployment "api-deployment" rollout to finish: 2 out of 3 new replicas have been updated...
Waiting for deployment "api-deployment" rollout to finish: 1 old replicas are pending termination...
Waiting for deployment "api-deployment" rollout to finish: 1 old replicas are pending termination...
Waiting for deployment "api-deployment" rollout to finish: 1 old replicas are pending termination...
deployment "api-deployment" successfully rolled out
$ kubectl rollout history deployment/api-deployment
deployment.apps/api-deployment
REVISION CHANGE-CAUSE
1 <none>
3 <none>
4 <none>
5 <none>
While paused, no new ReplicaSet appeared. Resuming produced a single revision, 5, carrying both
changes. Revision 2 is missing because the undo in task 3 re-used the 1.26 template and
renumbered it as revision 4.
- Write a second Deployment,
recreate-demo, with threenginx:1.25replicas and theRecreatestrategy. Change its image and look at the Pods straight away, then at the Deployment’s scaling events.
Solution
# deployment-recreate.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: recreate-demo
spec:
replicas: 3
strategy:
type: Recreate
selector:
matchLabels:
app: recreate-demo
template:
metadata:
labels:
app: recreate-demo
spec:
containers:
- name: web
image: nginx:1.25
$ kubectl apply -f deployment-recreate.yaml
deployment.apps/recreate-demo created
$ kubectl rollout status deployment/recreate-demo
Waiting for deployment "recreate-demo" rollout to finish: 0 of 3 updated replicas are available...
Waiting for deployment "recreate-demo" rollout to finish: 1 of 3 updated replicas are available...
Waiting for deployment "recreate-demo" rollout to finish: 2 of 3 updated replicas are available...
deployment "recreate-demo" successfully rolled out
$ kubectl set image deployment/recreate-demo web=nginx:1.26
deployment.apps/recreate-demo image updated
$ kubectl get pods -l app=recreate-demo
NAME READY STATUS RESTARTS AGE
recreate-demo-7b95fdc47d-b8kdh 1/1 Terminating 0 2s
recreate-demo-7b95fdc47d-d4pks 1/1 Terminating 0 2s
recreate-demo-7b95fdc47d-lz9w7 1/1 Terminating 0 2s
$ kubectl rollout status deployment/recreate-demo
Waiting for deployment "recreate-demo" rollout to finish: 0 out of 3 new replicas have been updated...
Waiting for deployment "recreate-demo" rollout to finish: 0 out of 3 new replicas have been updated...
Waiting for deployment "recreate-demo" rollout to finish: 0 out of 3 new replicas have been updated...
Waiting for deployment "recreate-demo" rollout to finish: 0 out of 3 new replicas have been updated...
Waiting for deployment "recreate-demo" rollout to finish: 0 out of 3 new replicas have been updated...
Waiting for deployment "recreate-demo" rollout to finish: 0 of 3 updated replicas are available...
Waiting for deployment "recreate-demo" rollout to finish: 1 of 3 updated replicas are available...
Waiting for deployment "recreate-demo" rollout to finish: 2 of 3 updated replicas are available...
deployment "recreate-demo" successfully rolled out
$ kubectl describe deployment recreate-demo | grep ScalingReplicaSet
Normal ScalingReplicaSet 8s deployment-controller Scaled up replica set recreate-demo-7b95fdc47d from 0 to 3
Normal ScalingReplicaSet 6s deployment-controller Scaled down replica set recreate-demo-7b95fdc47d from 3 to 0
Normal ScalingReplicaSet 3s deployment-controller Scaled up replica set recreate-demo-7467bbd55f from 0 to 3
All three old Pods are Terminating and no new Pod exists yet. The events confirm the order:
the old ReplicaSet goes to 0 before the new one starts, so there is a window with nothing
serving. That is the cost of guaranteeing two versions never run at once.
Clean up with kubectl delete deployment api-deployment recreate-demo.