Most of the workloads you meet early in Kubernetes are interchangeable: any replica can serve any request, pods can come and go in any order, and if one dies the ReplicaSet spins up a fresh one with a new random name and nobody cares. That is a Deployment, and it is the right model for a stateless web tier. But some workloads are not like that. A database primary has to start before its replicas. A clustered message broker needs each node to keep the same disk across restarts. A quorum member needs a stable name so its peers can find it again. For those, Kubernetes has the StatefulSet — and the CKA expects you to know exactly what it guarantees, what it does not, and how to spot the scenario that demands one.
This guide walks through the mechanics that actually show up in tasks: stable identity, the headless Service, per-pod storage with volumeClaimTemplates, ordered create/scale/update behaviour, podManagementPolicy, and staged rollouts with partition. If you want the wider map of the exam first, keep the CKA exam guide alongside this, and for how the interchangeable-pod controllers work, the Workloads & Scheduling guide covers Deployments and DaemonSets.
What a StatefulSet Guarantees (and a Deployment Does Not)
A StatefulSet manages a set of pods that each carry a persistent, sticky identity. Where a Deployment treats its pods as a herd, a StatefulSet treats them as named individuals numbered 0 to N-1 — the ordinal index. Those three guarantees are the whole point:
| Guarantee | Deployment | StatefulSet |
|---|---|---|
| Pod name | Random suffix (web-6d4cf56db6-x8k2p) | Stable, ordinal-based (web-0, web-1, web-2) |
| Network identity | Changes every recreate | Stable DNS hostname that survives reschedule |
| Storage | Shared or ephemeral; a new pod gets nothing back | Each pod keeps its own PersistentVolume across restarts |
| Ordering | Pods created/updated in parallel, no order | Created 0→N-1, deleted N-1→0, one at a time |
The mental model: a Deployment answers “how many replicas?”; a StatefulSet answers “which replica, with which disk, in which order?” When a scenario cares about the which and the order, you are looking at a StatefulSet.
Exam signal: phrases like “each instance needs its own persistent storage”, “stable network identity”, “the primary must start before the replicas”, or “pods must be created in order” point at a StatefulSet. Words like “stateless”, “any replica can handle the request”, or “horizontally scalable web front end” point back at a Deployment.
The Headless Service: Identity’s Prerequisite
A StatefulSet does not create stable DNS on its own — it needs a headless Service to publish per-pod records, and you wire them together with the serviceName field. A headless Service is just a Service with clusterIP: None: instead of load-balancing to one virtual IP, it returns DNS records for the individual pods behind it.
apiVersion: v1
kind: Service
metadata:
name: nginx # this name becomes part of every pod's DNS
labels:
app: nginx
spec:
clusterIP: None # <-- headless: no single VIP, per-pod DNS instead
selector:
app: nginx
ports:
- port: 80
name: web
Each pod then gets a predictable hostname of the form <pod-name>.<service-name>.<namespace>.svc.cluster.local — for example web-0.nginx.default.svc.cluster.local. That name is stable: reschedule web-0 onto another node and peers can still resolve it. The exact DNS record shapes and how CoreDNS resolves them are covered in depth in the CoreDNS & service discovery guide — for StatefulSets, the thing to lock in is simply that no headless Service means no stable identity, and forgetting serviceName is one of the most common ways a StatefulSet manifest fails validation or behaves oddly.
Per-Pod Storage with volumeClaimTemplates
This is the mechanism candidates most often skip, and it is exactly where exam tasks like to probe. In a Deployment you attach storage by referencing an existing PersistentVolumeClaim — and every replica shares that one claim. A StatefulSet does the opposite: you give it a template, and it mints a separate PersistentVolumeClaim for each pod.
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: web
spec:
serviceName: nginx # must match the headless Service
replicas: 3
selector:
matchLabels:
app: nginx
template:
metadata:
labels:
app: nginx
spec:
containers:
- name: nginx
image: nginx:1.27
ports:
- containerPort: 80
name: web
volumeMounts:
- name: data
mountPath: /usr/share/nginx/html
volumeClaimTemplates: # one PVC minted per pod, not shared
- metadata:
name: data
spec:
accessModes: ["ReadWriteOnce"]
storageClassName: standard
resources:
requests:
storage: 1Gi
With replicas: 3, the controller creates three PVCs named after the template, the StatefulSet, and the ordinal: data-web-0, data-web-1, data-web-2. Each binds to its own PersistentVolume. The behaviour you must remember for the exam:
- The disk follows the identity. If
web-1is deleted and rescheduled, it re-attachesdata-web-1— the same data, not a fresh volume. That is the entire reason StatefulSets exist. - PVCs are not garbage-collected by default. Delete the StatefulSet, or scale it down, and the PVCs (and their data) stay behind. This is a safety feature — Kubernetes will not throw away your database — but it means orphaned volumes accumulate silently. A newer field,
persistentVolumeClaimRetentionPolicy(withwhenDeletedandwhenScaledset toRetainorDelete), lets you opt into automatic cleanup; the default for both isRetain. volumeClaimTemplatescannot be modified on a live StatefulSet by default. The API server rejects changes to it — only a handful of fields (such asreplicas,template,updateStrategy, andpersistentVolumeClaimRetentionPolicy) are mutable. To change storage size or class you typically delete and recreate the object (often with--cascade=orphanto preserve the pods), which is a classic gotcha.
If PersistentVolumes, claims and StorageClasses are hazy, the CKA storage guide is the prerequisite read — StatefulSets assume you already understand the four-resource storage model.
Ordered Creation, Scaling and Termination
By default a StatefulSet operates one pod at a time, in order. This is podManagementPolicy: OrderedReady, the default:
- Scale up / create: pods come up in ascending ordinal order —
web-0, thenweb-1, thenweb-2. Crucially, the controller waits for each pod to be Running and Ready before starting the next. Ifweb-0never becomes Ready,web-1never even gets created. - Scale down / delete: pods are removed in descending order —
web-2first, thenweb-1, thenweb-0— again one at a time, each fully terminated before the next.
That ordering is precisely what a primary/replica database or a quorum-based system needs: the lowest ordinal (often the “seed” or “primary”) is guaranteed to exist and be healthy before the others join.
The trade-off is head-of-line blocking: a single pod that is stuck in Pending or crash-looping and never reaches Ready will freeze the entire rollout. On the exam, if you scale a StatefulSet to 5 and only see web-0 through web-2, your first move is kubectl describe pod web-3 — the blocker is almost always an unschedulable pod or an unbound PVC.
When strict ordering is not required, you can switch to podManagementPolicy: Parallel, which launches and terminates all pods at once, exactly like a Deployment’s speed but keeping stable identity and storage. It is set at creation time and cannot be changed later.
spec:
podManagementPolicy: Parallel # bring all pods up/down together; keep identity + storage
Update Strategies: RollingUpdate and partition
StatefulSets support two update strategies, controlled by spec.updateStrategy.type:
| Strategy | Behaviour | When to use |
|---|---|---|
| RollingUpdate (default) | Updates pods automatically in reverse ordinal order (N-1 → 0), one at a time, waiting for each to be Ready | Normal upgrades where identity/order matter |
| OnDelete | Controller does not update pods automatically; you manually delete a pod and it is recreated with the new spec | Fully manual/controlled upgrades, or older tooling |
RollingUpdate also supports a partition, which is the StatefulSet’s built-in canary mechanism. Only pods with an ordinal greater than or equal to the partition value are updated; everything below the partition stays on the old spec.
spec:
updateStrategy:
type: RollingUpdate
rollingUpdate:
partition: 2 # only web-2 and above get the new spec; web-0, web-1 stay put
Set partition: 2 on a 3-replica set and change the image: only web-2 rolls to the new version. Verify it is healthy, then lower the partition to 1, then 0, walking the rollout down the ordinals under your control. Set the partition higher than the replica count and you effectively pause all updates. This is a favourite exam-and-interview detail because it inverts the usual “higher number = more updated” intuition — with partition, a higher value updates fewer pods.
StatefulSet vs Deployment: The Decision
Bring it together into the choice the exam actually tests:
| The scenario says… | Choose | Because |
|---|---|---|
| Stateless API, any replica serves any request | Deployment | Pods are fungible; no identity or storage stickiness needed |
| Each replica needs its own durable volume | StatefulSet | volumeClaimTemplates gives per-pod storage that survives reschedule |
| Pods must start in a defined order (primary first) | StatefulSet | OrderedReady guarantees ascending, gated startup |
| Peers address each other by stable hostname | StatefulSet + headless Service | Stable DNS identity per pod |
| One pod per node (log/metrics agent) | DaemonSet | Node-scoped, not replica-scoped — see the workloads guide |
| Run-to-completion batch job | Job / CronJob | Not a long-running controller at all |
A quick sanity rule: if losing a pod and getting a brand-new anonymous one back would be fine, you want a Deployment. If the replacement pod must be “the same one” — same name, same disk — you want a StatefulSet.
A Minimal End-to-End Walkthrough
Putting the pieces together as you would in a task — headless Service first, then the StatefulSet that references it:
# 1. Create the headless Service and StatefulSet
kubectl apply -f nginx-headless-svc.yaml
kubectl apply -f web-statefulset.yaml
# 2. Watch ordered creation (0, then 1, then 2)
kubectl get pods -l app=nginx -w
# 3. Confirm each pod got its own PVC
kubectl get pvc
# data-web-0 Bound ...
# data-web-1 Bound ...
# data-web-2 Bound ...
# 4. Prove identity + storage stickiness: delete a pod, it comes back the same
kubectl delete pod web-1
kubectl get pod web-1 -o wide # same name, re-attaches data-web-1
# 5. Scale and observe reverse-order termination
kubectl scale statefulset web --replicas=2 # web-2 terminates; data-web-2 stays
# 6. Staged image update with a partition canary
kubectl patch statefulset web --type='json' \
-p='[{"op":"replace","path":"/spec/updateStrategy/rollingUpdate/partition","value":2}]'
kubectl set image statefulset/web nginx=nginx:1.28 # only web-2 updates
Step 5 is the one to internalise: after scaling down, run kubectl get pvc and you will still see data-web-2. The pod is gone; the disk is not. That surprises people in production and it is a deliberate exam trap.
Common StatefulSet Mistakes on the CKA
- Omitting the headless Service or
serviceName. Without it there is no stable DNS, and the StatefulSet will not behave as expected. The Service must exist and match. - Expecting PVCs to disappear. Scaling down or deleting the StatefulSet leaves PVCs (and PVs) behind by default. Clean up explicitly, or set a
persistentVolumeClaimRetentionPolicy. - A stuck pod freezing the rollout. With
OrderedReady, one un-Ready pod blocks everything above it. Diagnose the blocking ordinal, don’t stare at the ones that never appeared. - Misreading
partition. Higher partition = fewer pods updated. Set it above the replica count to pause updates entirely. - Trying to edit
volumeClaimTemplatesin place. It is immutable; recreate the StatefulSet (consider--cascade=orphan) to change storage. - Reaching for a StatefulSet by reflex for anything with a volume. A single shared
ReadWriteManyvolume across a stateless fleet is still a Deployment job. StatefulSets are for per-pod identity and storage.
Where This Fits in Your CKA Prep
StatefulSets sit in the Workloads & Scheduling domain, and they connect directly to storage and services — three areas the CKA loves to combine into one task (“deploy a 3-replica stateful app, each with its own 1Gi volume, addressable by stable hostname”). The fastest way to make the behaviour automatic is to build one, delete pods, scale it, and watch the PVCs — muscle memory beats memorisation on a performance-based exam.
Once the manifest mechanics feel natural, pressure-test them under time. Sailor’s CKA Certification-Ready Mock Exam Bundle runs full-length, timed, hands-on-style scenarios that mix StatefulSets with storage and services exactly the way the real exam does, so you practise the combination rather than each piece in isolation. Pair that with the Workloads & Scheduling guide for the controllers around it and the storage guide for the volumes underneath it.
Frequently Asked Questions
What is the difference between a StatefulSet and a Deployment?
A Deployment manages interchangeable, anonymous pods with random names and shared or ephemeral storage, created and updated in parallel. A StatefulSet gives each pod a stable ordinal identity (web-0, web-1), a stable DNS hostname via a headless Service, and its own PersistentVolume that survives rescheduling — and it operates in a defined order. Use a Deployment for stateless apps and a StatefulSet when identity, ordering, or per-pod storage matters.
Why does a StatefulSet need a headless Service?
The headless Service (clusterIP: None) is what publishes per-pod DNS records, giving each pod a stable, resolvable hostname that survives reschedule. The StatefulSet links to it through the serviceName field. Without it, pods have no stable network identity — which defeats the main reason to use a StatefulSet.
What happens to PersistentVolumeClaims when I delete a StatefulSet?
By default they are retained, along with the data on the underlying PersistentVolumes — Kubernetes deliberately does not delete your storage. You either clean up the PVCs manually or configure persistentVolumeClaimRetentionPolicy to have them removed when the StatefulSet is deleted or scaled down.
What does the partition field do in a StatefulSet update?
With a RollingUpdate strategy, partition: N means only pods with an ordinal ≥ N are updated to the new spec; lower ordinals stay on the old version. It is a canary/staged-rollout control. Counter-intuitively, a higher partition updates fewer pods, and a partition above the replica count pauses updates entirely.
When should I use podManagementPolicy: Parallel?
Use Parallel when you still need stable identity and per-pod storage but do not need pods created or deleted in strict order — it starts and stops all pods at once, like a Deployment’s speed. Keep the default OrderedReady when startup order matters, such as a primary that must be ready before replicas join.
Can I change volumeClaimTemplates after creating a StatefulSet?
By default, no — the API server rejects changes to volumeClaimTemplates on a live StatefulSet (only fields such as replicas, template, updateStrategy, and persistentVolumeClaimRetentionPolicy are mutable). To change the storage size, class, or access mode you delete and recreate the StatefulSet — often with --cascade=orphan so the running pods are preserved while you swap the controller.
Conclusion
A StatefulSet is Kubernetes’ answer to workloads whose pods are not interchangeable: it hands each pod a stable name, a stable DNS identity through a headless Service, and its own durable volume through volumeClaimTemplates, and it creates, scales, and updates them in a predictable order. On the CKA, the winning skills are recognising the scenario (“own storage per replica”, “ordered startup”, “stable hostname”), wiring the headless Service correctly, and remembering the two behaviours that trip everyone up — PVCs persist after scale-down, and a stuck pod blocks an ordered rollout. Build one, break it, and scale it a few times, and the pattern becomes second nature well before exam day.