Deployments, Rollouts and the Other Workload Types
How a Deployment updates without downtime, what maxSurge and maxUnavailable really control, and the workload types most tutorials skip.
Three Objects, One Chain
A Deployment doesn't manage pods. It manages ReplicaSets, and those manage pods:
Understanding the middle layer explains how rollback works. When you change the pod template, the Deployment creates a new ReplicaSet and gradually shifts replicas from the old to the new. The old ReplicaSet isn't deleted — it's scaled to zero and kept.
That retained object is the rollback. kubectl rollout undo scales the previous ReplicaSet back up. It's not re-deploying from a stored manifest; the old configuration is still sitting there.
kubectl get replicasets
# NAME DESIRED CURRENT READY AGE
# api-7d4f8b9c5 3 3 3 2m
# api-5c8f7a2b1 0 0 0 1h <- previous version, keptrevisionHistoryLimit controls how many are kept; the default is 10.
A change only triggers a rollout if it changes the pod template (spec.template). Editing replicas scales the existing ReplicaSet without creating a new revision — which is correct, since scaling isn't a new version.
Rolling Updates
The default strategy replaces pods gradually, governed by two fields:
spec:
strategy:
type: RollingUpdate
rollingUpdate:
maxSurge: 25% # extra pods allowed ABOVE the desired count
maxUnavailable: 25% # pods allowed to be missing BELOW itBoth default to 25%. So a 4-replica Deployment may run up to 5 pods during a rollout and must keep at least 3 available.
The two knobs trade speed against capacity:
| Setting | Effect |
|---|---|
maxSurge: 0 | Never exceed the replica count — for tight resource budgets. Slower. |
maxUnavailable: 0 | Never drop below full capacity. Requires maxSurge > 0. Safest. |
| Both high | Fastest rollout, largest capacity dip |
maxUnavailable: 0 with maxSurge: 1 is a common choice for a service that must not lose capacity: bring up one new pod, wait for it to be ready, then retire one old pod.
Rolling updates depend entirely on readiness probes. Without one, a pod counts as available the moment its container starts — so Kubernetes cheerfully retires an old pod and replaces it with one that isn't ready to serve. Your "zero-downtime" rollout drops requests. The next guide is about exactly this.
The Recreate strategy
spec:
strategy:
type: RecreateTerminate everything, then start the new version. That means downtime — chosen deliberately when two versions genuinely cannot coexist: an incompatible schema migration, or a workload holding an exclusive lock. Honest downtime beats subtle corruption.
Driving and Observing a Rollout
kubectl rollout status deploy/api # block until complete or failed
kubectl rollout history deploy/api # revisions
kubectl rollout undo deploy/api # back one revision
kubectl rollout undo deploy/api --to-revision=3
kubectl rollout restart deploy/api # restart pods without changing the imagekubectl rollout restart is genuinely useful — it patches an annotation on the pod template, which creates a new ReplicaSet and rolls pods safely. It's the supported way to pick up a changed ConfigMap or Secret, which the configuration guide returns to.
If a rollout stalls, progressDeadlineSeconds is what marks it as failed rather than leaving it pending forever.
Check yourself
A Deployment with 4 replicas uses the defaults. Mid-rollout, how many pods can exist and how many must stay available?
DaemonSets
One pod per node, automatically. New node joins, it gets a pod; node leaves, the pod goes.
apiVersion: apps/v1
kind: DaemonSet
metadata:
name: log-collector
spec:
selector:
matchLabels: { app: log-collector }
template:
metadata:
labels: { app: log-collector }
spec:
tolerations:
- operator: Exists # run on tainted nodes too, including control plane
containers:
- name: collector
image: fluent-bit:3.1For infrastructure that must be everywhere: log collectors, metrics agents, CNI plugins, storage drivers. Note the toleration — without it, a DaemonSet skips tainted nodes, which usually defeats the purpose for a log collector.
Jobs
Run to completion, then stop.
apiVersion: batch/v1
kind: Job
metadata:
name: migrate
spec:
backoffLimit: 4
template:
spec:
restartPolicy: OnFailure
containers:
- name: migrate
image: myapp:1.4.0
command: ["npm", "run", "migrate"]backoffLimit caps retries before the Job is marked failed. completions and parallelism handle batch work — run N items, M at a time.
A Job's pods are not deleted when it completes — that's deliberate, so you can read their logs. They accumulate. ttlSecondsAfterFinished cleans them up automatically, and it's worth setting on anything that runs regularly.
CronJobs
A Job on a schedule.
apiVersion: batch/v1
kind: CronJob
metadata:
name: nightly-report
spec:
schedule: "0 2 * * *"
concurrencyPolicy: Forbid
successfulJobsHistoryLimit: 3
failedJobsHistoryLimit: 3
jobTemplate:
spec:
template:
spec:
restartPolicy: OnFailure
containers:
- name: report
image: myapp:1.4.0
command: ["npm", "run", "report"]concurrencyPolicy is the field to think about, because the default surprises people:
Allow(default) — a new run starts even if the previous one is still going. A job that occasionally overruns its interval will pile up.Forbid— skip the new run if the previous is still active.Replace— kill the running one and start fresh.
Two things that catch people out. CronJob schedules use the kube-controller-manager's timezone unless you set timeZone explicitly — so "2am" may not be the 2am you meant. And if the controller is down long enough to miss a scheduled time, the run is skipped rather than backfilled.
Check yourself
A CronJob runs every 5 minutes. Under load, a run occasionally takes 12 minutes. With default settings, what happens?
Choosing a Workload Type
| Need | Use |
|---|---|
| Stateless app, interchangeable replicas | Deployment |
| Stable identity, per-replica storage | StatefulSet (phase 4) |
| One pod on every node | DaemonSet |
| Run once to completion | Job |
| Run on a schedule | CronJob |
What's Next
Rolling updates only work if Kubernetes can tell a ready pod from a running one. The next guide covers the three probes that make that distinction — and why the most common probe mistake takes down a whole fleet instead of one pod.