Deployments and ReplicaSets
Master Deployments for managing application lifecycle. Learn scaling, rolling updates, and rollbacks.
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:
| Field | Description |
|---|---|
replicas: 3 | Run 3 copies of this Pod |
selector.matchLabels | How the Deployment finds its Pods |
template | The Pod specification (same as a Pod YAML) |
template.metadata.labels | Must 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:
- Deployment creates a new ReplicaSet with the updated image
- New ReplicaSet scales up gradually (new pods start coming alive)
- Old ReplicaSet scales down gradually (old pods retire gracefully)
- 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
| Setting | Description |
|---|---|
maxSurge: 1 | Allow 1 extra Pod during update (so 4 total when replicas=3) |
maxUnavailable: 0 | Never 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
| Condition | Meaning |
|---|---|
Available | Minimum required Pods are running |
Progressing | Deployment 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!