Deployment: a controller on top of ReplicaSet
How Deployment manages a ReplicaSet, what the READY and UP-TO-DATE columns mean, and which fields real manifests set.
How Deployment manages a ReplicaSet, what the READY, UP-TO-DATE, and
AVAILABLE columns mean, and which fields show up in real manifests.
Layer on top of layer
In Kubernetes, controllers nest like matryoshka dolls. A Pod is one copy of an app. A ReplicaSet keeps N such copies running. A Deployment manages a ReplicaSet and adds one more thing on top: updating versions without downtime.
When you run kubectl apply -f deploy.yaml, here's what happens:
- A Deployment object gets created.
- The Deployment controller creates a ReplicaSet with the needed template.
- The ReplicaSet controller creates Pods from the template.
The image in the Deployment changes, and the controller creates a new ReplicaSet while gradually winding down the old one. The old one's replica count drifts to 0, the new one's grows to N. This is a rolling update, and it gets its own lesson.
Minimal manifest
apiVersion: apps/v1
kind: Deployment
metadata:
name: web
spec:
replicas: 3
selector:
matchLabels:
app: web
template:
metadata:
labels:
app: web
spec:
containers:
- name: web
image: nginx:1.27.3-alpine
ports:
- containerPort: 80
Compared to a ReplicaSet, the top-level fields are almost identical. The
same apps/v1, the same selector.matchLabels, the same template. The
difference is kind: Deployment and a few optional fields that a
ReplicaSet doesn't have.
The rules for the selector are the same as for a ReplicaSet. The template's labels must match the selector, otherwise apply fails:
The Deployment "web" is invalid: spec.template.metadata.labels: Invalid value: {"app":"webapp"}: `selector` does not match template `labels`
You can't change a created Deployment's selector, the API answers with
field is immutable. You need a different selector, which means you need
a new Deployment.
spec.strategy
How to roll out a new version. The default is RollingUpdate (gradual),
there's also Recreate (kill everything, then bring it all back up). For
RollingUpdate you tune two parameters:
maxSurge: how many extra Pods abovereplicasare allowed during the update (25% by default).maxUnavailable: how many Pods can be killed before the new ones come up (25% by default).
At the start you don't need to write these fields, the defaults are reasonable.
spec.revisionHistoryLimit
How many old ReplicaSets to keep for rollback. 10 by default. Each old
ReplicaSet hangs around with replicas: 0, but it remembers the template
from that release. kubectl rollout undo uses exactly these: on rollback,
Deployment doesn't create a new ReplicaSet, it spins the old one back up.
spec.progressDeadlineSeconds
How many seconds to wait for an update to make progress before declaring it stuck. 600 by default, that is, 10 minutes. More on this in the "Conditions" section.
What kubectl shows
kubectl get deploy web
NAME READY UP-TO-DATE AVAILABLE AGE
web 3/3 3 3 6s
READY: how many Pods passed readiness out of the desired number.UP-TO-DATE: how many Pods were already created from the current template. Readiness isn't counted here.AVAILABLE: how many Pods count as available, that is, ready and have stayed ready for at leastminReadySeconds(0 by default).
| Column | What it counts | Less than replicas when |
|---|---|---|
READY | Pods that passed readiness | Pods are still starting or can't start |
UP-TO-DATE | Pods on the current template | a rolling update is in progress or stuck |
AVAILABLE | Ready longer than minReadySeconds | same as READY, plus Pods ready for less than minReadySeconds |
Next, look at the ReplicaSet the Deployment created:
kubectl get rs -l app=web
NAME DESIRED CURRENT READY AGE
web-854cf6f87f 3 3 3 6s
The ReplicaSet's name is built like this: <Deployment name>-<template hash>. The controller puts the same hash on the ReplicaSet and its Pods as
the pod-template-hash label, and the Pod's name also gets a random
suffix: web-854cf6f87f-8j74h. The hash changes every time the template
changes (image, env, anything). This is how Deployment knows which
ReplicaSet is the new one and which is old.
You don't scale a ReplicaSet that a Deployment owns by hand. If you run
kubectl scale rs web-854cf6f87f --replicas=1, Deployment almost
immediately returns it to the previous number: it reconciles the
ReplicaSet against its own spec.replicas. Delete the Deployment, and its
ReplicaSet and Pods get deleted with it.
Conditions: what Deployment itself says
A Deployment's status has a conditions array. The two main conditions:
Available=True: enough replicas are available (accounting formaxUnavailable).Progressing=True: an update or startup is in progress, everything is moving. After a successful update, the reason here isNewReplicaSetAvailable.
The easiest way to see them is kubectl describe:
kubectl describe deploy web | grep -A4 Conditions
Conditions:
Type Status Reason
---- ------ ------
Available True MinimumReplicasAvailable
Progressing True NewReplicaSetAvailable
If the new ReplicaSet gets stuck (for example, the image can't be pulled),
after progressDeadlineSeconds the Progressing condition flips to
False with the reason ProgressDeadlineExceeded. The old Pods keep
working meanwhile, so Available stays True. Here's what a Deployment
with 5 replicas looks like after being given an image that doesn't exist:
NAME READY UP-TO-DATE AVAILABLE AGE
web 4/5 3 4 108s
Available True MinimumReplicasAvailable
Progressing False ProgressDeadlineExceeded
Three new Pods sit in ErrImagePull or ImagePullBackOff, four old ones
serve traffic. Kubernetes itself doesn't roll back an update like this, it
only reports the problem. kubectl rollout status exits with an error in
this case:
error: deployment "web" exceeded its progress deadline
For the update to move forward, the Deployment's template needs to change again: fix the error or roll back to a previous revision.
kubectl scale and kubectl rollout
The most common actions on a Deployment:
kubectl scale deploy web --replicas=5
Changes the replica count on the fly.
kubectl rollout status deploy web
Waits for the current update to finish and shows how it's going.
kubectl rollout restart deploy web
Restarts all the Pods. The command puts the
kubectl.kubernetes.io/restartedAt annotation with the current time into
the template. The template changed, so a new ReplicaSet appears, and the
Pods get replaced following the rolling update rules.
kubectl rollout undo deploy web
Rolls back to the previous revision. kubectl rollout history deploy web
shows the list of revisions.
kubectl scale changes the Deployment's spec.replicas. The Deployment
passes the number to the current ReplicaSet, which adds or removes Pods.
The template doesn't change here, so no new ReplicaSet appears. If you
later apply an old file with replicas: 3, the number goes back to three.
What a real manifest sets at minimum
Real manifests almost always have these fields on a Deployment:
replicas, usually 2 or 3 for a web app;selector.matchLabels, matching the template's labels;template.spec.containerswith a fixed image tag andresources.requests/limits.
A ReplicaSet almost never gets written directly. You write a Deployment.
Common mistakes
- Template labels don't match the selector:
`selector` does not match template `labels`. - Trying to change the selector on a live Deployment:
field is immutable. - Running
kubectl scaleon the ReplicaSet instead of the Deployment. The Deployment silently returns it to the previous number. - Only looking at
READY. During a stuck update it can show a near-full number thanks to old Pods, whileUP-TO-DATEand theProgressingcondition show the truth. - Expecting Kubernetes to roll back a failed update on its own. It won't:
after
ProgressDeadlineExceededthe Deployment just stays half-updated until the template changes. - Treating
kubectl rollout undoas a universal button. A rollback throws away the needed changes from the new release too, not just the broken part.
What you'll need for the tasks
- A Deployment manifest:
apiVersion,replicas,selector,template. kubectl apply -f <file>,kubectl get deploy,kubectl get rs,kubectl get pod -l <selector>.kubectl describe deploy <name>: theConditionsandEventsblocks.kubectl describe pod <name>: theEventsblock, it shows why a Pod isn't starting.kubectl scale deploy/<name> --replicas=<N>,kubectl rollout status,kubectl rollout restart,kubectl rollout history.- A way to change one field in the template: editing the file and
kubectl apply,kubectl edit deploy <name>, orkubectl set image deploy/<name> <container>=<image>.
The tasks live in the labs/ folder: each lab brings up its own kind
cluster on your machine, instructions are in its README.md.
Homework
Practice the theory on your own cluster: 3 steps with a check. Set up the cluster with the Kubernetes locally track, the tasks live in the k8s-practice repository.
./run.sh start k8s-deployment-basics # set up the starting state
./run.sh verify k8s-deployment-basics # check the current step
In short
- Deployment manages a ReplicaSet, which in turn manages the Pods.
- A template change gives a new hash, a new ReplicaSet, and a rolling
update.
kubectl scaledoesn't change the template. READYis about Pod readiness,UP-TO-DATEis about matching the current template.- A stuck update shows up in conditions as
ProgressDeadlineExceeded, Kubernetes itself doesn't roll it back. revisionHistoryLimitsets how many revisions are available for rollback.
Interview-style questions
Interview questions on this topic: 1
Included with practice access, $49 one-time. Get access
Answer out loud or in writing, then reveal the answer and honestly mark whether you knew it.
Done reading?
Mark the lesson as read to move your track progress forward.
Lesson marked as done.
Go through it with an AI agent
The agent explains anything unclear in plain words, gives you 2 labs for this lesson and drills you on interview questions. $49 one-time, lifetime access.
Tell the agent: use MCP izi, give me lesson k8s-workloads/deployment
Progress saved in this browser
Clear your history or open this on your phone, and the marks will be gone. Leave your email and they'll move to your account. No password needed.
Done. moved to your account. You can see your progress on any device. My progress