Kubernetes Kubernetes

Deployments: Scaling, Self-Healing, and Rolling Updates

Run replicas of a Pod, update them without downtime, and roll back when a release goes wrong.

14 min Beginner

Pods are disposable, and a bare Pod that dies stays dead. A Deployment fixes that: you declare how many copies you want and which image to run, and Kubernetes keeps that many running and handles updates for you. This guide covers creating one, scaling it, updating it without downtime, and rolling it back.

Why you rarely create bare Pods

If a node fails or a Pod is deleted, nothing recreates a standalone Pod. In practice you almost never create Pods directly. You create a Deployment, which creates a ReplicaSet, which in turn creates and maintains the Pods. The Deployment adds what the ReplicaSet lacks: controlled updates and version history.

Write a Deployment

apiVersion: apps/v1
kind: Deployment
metadata:
  name: web
  labels:
    app: web
spec:
  replicas: 3
  selector:
    matchLabels:
      app: web
  template:
    metadata:
      labels:
        app: web
    spec:
      containers:
        - name: nginx
          image: nginx:1.25
          ports:
            - containerPort: 80

There are two metadata sections, and it helps to keep them straight. The top one describes the Deployment. The one under template describes the Pods it creates, and the template is simply a Pod manifest without a name. The selector tells the Deployment which Pods belong to it, so it must match the labels in the template, or Kubernetes will reject the manifest.

kubectl apply -f deployment.yaml
kubectl get deployment,replicaset,pods

You’ll see one Deployment, one ReplicaSet named after it plus a hash, and three Pods named after the ReplicaSet.

Labels and selectors: how objects find each other

Labels are key-value pairs you can attach to any object. Selectors query them, and this is how Kubernetes connects things: a Deployment finds its Pods, a Service finds its backends, and a network policy finds the Pods it governs.

kubectl get pods --show-labels
kubectl get pods -l app=web

To see how much a selector matters, remove the label from one Pod:

kubectl label pod POD_NAME app-
kubectl get pods --show-labels

The ReplicaSet no longer counts that Pod as one of its own, so it starts a replacement to get back to three. The relabeled Pod keeps running, but nothing manages it. Delete it with kubectl delete pod POD_NAME so it doesn’t linger.

Scale and watch self-healing

kubectl scale deployment web --replicas=5
kubectl get pods -w

In another terminal, delete a Pod and watch a replacement appear within moments. To change the replica count for good, edit replicas in the manifest and apply it again, since kubectl scale alone leaves your file out of date. You cannot get rid of a Deployment’s Pods by deleting them, because they keep coming back. Delete the Deployment itself, or scale it to zero.

Roll out a new version

Changing anything in the Pod template, such as the image, triggers a rolling update:

kubectl set image deployment/web nginx=nginx:1.26
kubectl rollout status deployment/web

The Deployment creates a second ReplicaSet for the new version, then gradually scales it up while scaling the old one down, so some Pods are always serving traffic. By default it allows up to 25% extra Pods (maxSurge) and up to 25% unavailable (maxUnavailable) during the change. You can tune this in the spec:

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

With maxUnavailable: 0, an old Pod is only removed once a new one is ready. What “ready” means is decided by readiness probes, covered in a later guide. Changing the replica count is not a rollout, because it doesn’t touch the Pod template.

Roll back

Kubernetes keeps the old ReplicaSets, which is what makes rollback quick:

kubectl rollout history deployment/web
kubectl rollout undo deployment/web
kubectl rollout undo deployment/web --to-revision=1

undo returns to the previous revision, and --to-revision picks a specific one. The number of old revisions kept is set by revisionHistoryLimit, 10 by default. One caution: if your manifests live in Git and something like Argo CD applies them, prefer reverting the commit over rollout undo. The undo changes the cluster but not Git, so the two disagree until someone reconciles them.

Popular Guides