Skip to main content
Kubernetes · lesson 3 of 10
Kubernetes intermediate Lesson 3 of 10

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

ObjectUse directly?Purpose
PodRarelySingle instance, no self-healing
ReplicaSetRarelyMaintains N replicas — managed by Deployment
DeploymentYes, alwaysManages 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, and strategy
  • Perform rolling updates and rollbacks with kubectl rollout
  • Scale deployments imperatively and declaratively
  • Explain Deployment → ReplicaSet → Pod hierarchy

Practice

  1. Apply deployment.yaml, scale it to 5 with kubectl scale, then run kubectl apply -f deployment.yaml again 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.

  1. Update the image to nginx:1.26 and 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.

  1. Roll out an image that doesn’t exist, nginx:9.99, with a 45-second timeout on rollout 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.

  1. Pause the Deployment, change both the image (nginx:1.27) and the CPU/memory limits, then resume. Use rollout history before 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.

  1. Write a second Deployment, recreate-demo, with three nginx:1.25 replicas and the Recreate strategy. 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.

Frequently Asked Questions

How is a Deployment different from a Pod?
A Pod is a single running workload unit. A Deployment manages Pods indirectly by creating and updating ReplicaSets to match the desired replica count.
What happens to traffic during a rolling update?
New pods start and pass their readiness probes before old pods are terminated. At no point does replica count drop to zero, so traffic continues uninterrupted.