Deployments and ReplicaSets

Master Deployments for managing application lifecycle. Learn scaling, rolling updates, and rollbacks.

8 min read

Deployments and ReplicaSets

In the Previous Tutorial, we created Pods and learned that they're mortal — they die and never come back. That's fine for experimenting, but in production? Yikes. You need something smarter managing your Pods.

Enter Deployments — the responsible adult in the room.

A Deployment manages Pods for you. It handles scaling, updates, and self-healing. If a Pod dies at 3 AM, the Deployment creates a new one before you even wake up. If you need 10 copies, the Deployment maintains exactly 10. It's like having a very efficient manager who never sleeps.

Why Not Just Pods?

I already know how to create Pods. Why do I need Deployments?

Because Pods are mortal, dude. They don't resurrect. Delete a Pod, it's gone forever. A node crashes, all Pods on it are lost. It's like writing a document without saving — one power outage and everything's gone.

For production workloads, you need something that:

  • Maintains a desired number of Pod replicas
  • Replaces failed Pods automatically (self-healing!)
  • Updates Pods without downtime
  • Rolls back if something goes wrong

That's exactly what Deployments do. Trust me, you'll never create naked Pods in production again.

Deployment → ReplicaSet → Pods

The hierarchy works like a chain of command at a company:

Deployment          (CEO - gives the orders)
    └── ReplicaSet     (Manager - makes sure work gets done)
            ├── Pod     (Worker)
            ├── Pod     (Worker)
            └── Pod     (Worker)
  • Deployment: The controller you interact with. You tell it what you want.
  • ReplicaSet: The middle manager. Ensures the right number of Pods are running. You rarely interact with these directly — they work behind the scenes.
  • Pods: The actual workloads doing the real work.

When you update a Deployment, it creates a new ReplicaSet with the updated Pod spec and gradually shifts Pods from the old ReplicaSet to the new one. Pretty slick, right?

Create a Deployment

Let's create our first Deployment! Create nginx-deployment.yaml:

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

That YAML looks familiar — it's like a Pod definition with extra stuff!

Exactly! The template section IS a Pod definition. The Deployment just wraps it with replica count and selector info. Let's break it down:

FieldDescription
replicas: 3Run 3 copies of this Pod
selector.matchLabelsHow the Deployment finds its Pods
templateThe Pod specification (same as a Pod YAML)
template.metadata.labelsMust match selector.matchLabels

Apply it:

kubectl apply -f nginx-deployment.yaml

Output:

deployment.apps/nginx-deployment created

Inspect the Deployment

Let's see what we've created:

kubectl get deployments

Output:

NAME               READY   UP-TO-DATE   AVAILABLE   AGE
nginx-deployment   3/3     3            3           30s
  • READY: 3/3 means 3 of 3 desired Pods are running
  • UP-TO-DATE: Pods running the latest spec
  • AVAILABLE: Pods ready to serve traffic

Check the Pods:

kubectl get pods

Output:

NAME                                READY   STATUS    RESTARTS   AGE
nginx-deployment-7c5ddbdf54-4xk2p   1/1     Running   0          45s
nginx-deployment-7c5ddbdf54-9qm8r   1/1     Running   0          45s
nginx-deployment-7c5ddbdf54-xvn7t   1/1     Running   0          45s

Notice the naming pattern: deployment-name + replicaset-hash + pod-hash. It looks like gibberish, but it's actually Kubernetes keeping track of which ReplicaSet created which Pod.

Check the ReplicaSet (the middle manager):

kubectl get replicasets

Output:

NAME                          DESIRED   CURRENT   READY   AGE
nginx-deployment-7c5ddbdf54   3         3         3       1m

Self-Healing in Action

Here's where it gets magical. Let's kill a Pod and watch Kubernetes bring it back from the dead:

# In one terminal, watch pods (this is going to be fun)
kubectl get pods --watch

# In another terminal, kill a pod (RIP)
kubectl delete pod nginx-deployment-7c5ddbdf54-4xk2p

Watch the output:

NAME                                READY   STATUS        AGE
nginx-deployment-7c5ddbdf54-4xk2p   1/1     Terminating   2m
nginx-deployment-7c5ddbdf54-9qm8r   1/1     Running       2m
nginx-deployment-7c5ddbdf54-xvn7t   1/1     Running       2m
nginx-deployment-7c5ddbdf54-abc12   0/1     Pending       0s
nginx-deployment-7c5ddbdf54-abc12   1/1     Running       3s

The Deployment noticed a Pod was missing and immediately created a replacement. Always 3 Pods running. It's like a bouncer at a club — "We need exactly 3 people inside at all times. Someone left? Let the next person in."

How cool is that?

Scaling

Need more Pods? Need fewer? Scaling is embarrassingly easy.

Scale Imperatively

The quick way from the command line:

kubectl scale deployment nginx-deployment --replicas=5

Check:

kubectl get pods

Now 5 Pods running. Just like that. No downtime, no drama.

Scale Declaratively

Edit nginx-deployment.yaml:

spec:
  replicas: 5  # Changed from 3

Apply:

kubectl apply -f nginx-deployment.yaml

Or edit directly:

kubectl edit deployment nginx-deployment

This opens the deployment in your editor. Change replicas, save, and it takes effect immediately.

Rolling Updates

Okay, THIS is the killer feature of Deployments. You can update your application without any downtime. Zero. Zilch. Nada.

Update the Image

Let's update nginx from 1.24 to 1.25:

kubectl set image deployment/nginx-deployment nginx=nginx:1.25

Or edit the YAML and apply. Watch the rollout:

kubectl rollout status deployment/nginx-deployment

Output:

Waiting for deployment "nginx-deployment" rollout to finish: 2 out of 3 new replicas have been updated...
Waiting for deployment "nginx-deployment" rollout to finish: 2 out of 3 new replicas have been updated...
Waiting for deployment "nginx-deployment" rollout to finish: 1 old replicas are pending termination...
deployment "nginx-deployment" successfully rolled out

What's Happening Behind the Scenes?

This is where the magic happens. Here's the choreography:

  1. Deployment creates a new ReplicaSet with the updated image
  2. New ReplicaSet scales up gradually (new pods start coming alive)
  3. Old ReplicaSet scales down gradually (old pods retire gracefully)
  4. At all times, minimum Pods are available (no downtime!)

It's like swapping crew members on a ship one at a time — the ship never stops sailing.

Check ReplicaSets:

kubectl get replicasets
NAME                          DESIRED   CURRENT   READY   AGE
nginx-deployment-7c5ddbdf54   0         0         0       10m
nginx-deployment-85b98978d    3         3         3       1m

Old ReplicaSet still exists (Kubernetes keeps it around for rollback — smart!) but has 0 Pods.

Rollout Strategy

You can fine-tune exactly how updates happen. Want zero downtime? You got it:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx-deployment
spec:
  replicas: 3
  strategy:
    type: RollingUpdate
    rollingUpdate:
      maxSurge: 1        # Max pods over desired count during update
      maxUnavailable: 0  # Max pods that can be unavailable during update
  selector:
    matchLabels:
      app: nginx
  template:
    # ... rest of spec
SettingDescription
maxSurge: 1Allow 1 extra Pod during update (so 4 total when replicas=3)
maxUnavailable: 0Never have fewer than 3 Pods available

This means: always have at least 3 Pods ready. Zero downtime. Your users won't even notice you deployed.

Alternative strategy — Recreate (the brutal approach):

strategy:
  type: Recreate

Kills all old Pods before creating new ones. Yes, this causes downtime. Use only when you can't run two versions simultaneously (like database schema conflicts). Otherwise, stick with RollingUpdate.

Rollback

Oops, deployed a bad version? No worries — Kubernetes saved your previous versions. Rolling back is one command away.

View History

kubectl rollout history deployment/nginx-deployment

Output:

REVISION  CHANGE-CAUSE
1         <none>
2         <none>

Add change cause annotations for better tracking:

kubectl annotate deployment/nginx-deployment kubernetes.io/change-cause="Updated to nginx 1.25"

Rollback to Previous

kubectl rollout undo deployment/nginx-deployment

That's it. One command and you're back to the working version. No panic. No sweat.

Rollback to Specific Revision

kubectl rollout undo deployment/nginx-deployment --to-revision=1

Check Rollback Status

kubectl rollout status deployment/nginx-deployment

Pause and Resume

Need to make multiple changes at once without triggering a rollout for each one? Pause the Deployment first:

kubectl rollout pause deployment/nginx-deployment

Make multiple changes:

kubectl set image deployment/nginx-deployment nginx=nginx:1.26
kubectl set resources deployment/nginx-deployment -c=nginx --limits=memory=256Mi

Resume:

kubectl rollout resume deployment/nginx-deployment

All changes apply in one single rollout instead of triggering multiple. It's like batching your edits before hitting "save."

Deployment Conditions

Check deployment status:

kubectl describe deployment nginx-deployment

Look at the Conditions section:

Conditions:
  Type           Status  Reason
  ----           ------  ------
  Available      True    MinimumReplicasAvailable
  Progressing    True    NewReplicaSetAvailable
ConditionMeaning
AvailableMinimum required Pods are running
ProgressingDeployment is creating/scaling ReplicaSets

Common Commands

# Create/update deployment
kubectl apply -f deployment.yaml

# Check status
kubectl get deployments
kubectl describe deployment <name>

# Scale
kubectl scale deployment <name> --replicas=5

# Update image
kubectl set image deployment/<name> <container>=<image>

# Watch rollout
kubectl rollout status deployment/<name>

# View history
kubectl rollout history deployment/<name>

# Rollback
kubectl rollout undo deployment/<name>

# Pause/resume
kubectl rollout pause deployment/<name>
kubectl rollout resume deployment/<name>

Clean Up

kubectl delete deployment nginx-deployment

This deletes the Deployment, its ReplicaSets, and all Pods. Gone. Clean slate.

What's Next?

Awesome — you can now run Pods, and Deployments keep them alive and updated. But here's the thing: how do you actually access these Pods? They have internal IPs that change every time they restart. You can't exactly hand your users a Pod IP address.

In the next tutorial, you'll learn about Services — stable endpoints that route traffic to your Pods no matter what happens behind the scenes. Let's go!