StatefulSet vs Deployment
See why StatefulSets exist by comparing the same app deployed both ways.
Time: ~15 minutes Difficulty: Intermediate
What You Will Learn
Section titled “What You Will Learn”- Stable pod identity (
-0,-1) vs random pod names - Per-pod persistent storage via
volumeClaimTemplates - Init containers for one-time setup
- Headless services and why StatefulSets need them
- PVC lifecycle (survives pod deletion)
The Problem
Section titled “The Problem”A Deployment gives pods random names and uses shared or ephemeral storage. When a pod is deleted, the replacement gets a new name and starts with empty storage. This breaks workloads that need stable identity or persistent per-pod data (databases, caches, queues).
A StatefulSet solves this with ordered, stable pod names and dedicated PVCs that reattach on restart.
Deploy Both Versions
Section titled “Deploy Both Versions”Navigate to the demo directory:
cd demos/statefulsetkubectl apply -f manifests/namespace.yamlkubectl apply -f manifests/counter-script.yamlkubectl apply -f manifests/deployment-version.yamlkubectl apply -f manifests/statefulset-version.yamlWait for all pods to be ready:
kubectl get pods -n statefulset-demo -wYou should see pods like:
counter-deploy-7b8f9c-abc12(random suffix)counter-deploy-7b8f9c-xyz34(random suffix)counter-sts-0(stable name)counter-sts-1(stable name)
Test: Pod Identity
Section titled “Test: Pod Identity”Deployment pods get random names
Section titled “Deployment pods get random names”# Note the pod nameskubectl get pods -l variant=deployment -n statefulset-demo
# Delete a podkubectl delete pod -l variant=deployment -n statefulset-demo --wait=false
# Watch - the replacement has a different namekubectl get pods -l variant=deployment -n statefulset-demo -wStatefulSet pods keep their names
Section titled “StatefulSet pods keep their names”# Note the pod names (counter-sts-0, counter-sts-1)kubectl get pods -l variant=statefulset -n statefulset-demo
# Delete pod-0kubectl delete pod counter-sts-0 -n statefulset-demo
# Watch - counter-sts-0 comes back with the SAME namekubectl get pods -l variant=statefulset -n statefulset-demo -wTest: Storage Persistence
Section titled “Test: Storage Persistence”Deployment loses data on restart
Section titled “Deployment loses data on restart”# Check the boot count (will show 1)kubectl exec counter-deploy-<TAB> -n statefulset-demo -- cat /data/counter
# Delete the podkubectl delete pod -l variant=deployment -n statefulset-demo --wait=false
# Check the new pod's boot count (back to 1 - data was lost)kubectl exec <new-pod-name> -n statefulset-demo -- cat /data/counterStatefulSet preserves data across restarts
Section titled “StatefulSet preserves data across restarts”# Check the boot countkubectl exec counter-sts-0 -n statefulset-demo -- cat /data/counter
# Delete the podkubectl delete pod counter-sts-0 -n statefulset-demo
# Wait for it to come back, then check again (count incremented, not reset)kubectl wait --for=condition=Ready pod/counter-sts-0 -n statefulset-demo --timeout=60skubectl exec counter-sts-0 -n statefulset-demo -- cat /data/counterThe counter increments because the same PVC reattaches to the same pod.
Test: PVC Lifecycle
Section titled “Test: PVC Lifecycle”# See the per-pod PVCs created by volumeClaimTemplateskubectl get pvc -n statefulset-demo
# Output:# data-counter-sts-0 Bound ...# data-counter-sts-1 Bound ...Each pod gets its own PVC named <template-name>-<pod-name>. These PVCs survive pod deletion and even scale-down.
What is Happening
Section titled “What is Happening”manifests/ namespace.yaml # statefulset-demo namespace counter-script.yaml # ConfigMap with init script deployment-version.yaml # Deployment + Service (emptyDir, random names) statefulset-version.yaml # StatefulSet + headless Service (PVC per pod)Both versions run the same app: an init container reads a counter file, increments it, writes an HTML page, then nginx serves it. The difference is storage:
- Deployment: uses
emptyDir, lost on pod deletion - StatefulSet: uses
volumeClaimTemplates, persists across restarts
The StatefulSet also requires a headless Service (clusterIP: None) for stable DNS. Each pod gets a DNS entry: counter-sts-0.counter-sts.statefulset-demo.svc.cluster.local.
Experiment
Section titled “Experiment”-
Scale the StatefulSet and watch pods come up in order:
Terminal window kubectl scale statefulset counter-sts --replicas=4 -n statefulset-demokubectl get pods -l variant=statefulset -n statefulset-demo -w -
Scale back down and verify PVCs are NOT deleted:
Terminal window kubectl scale statefulset counter-sts --replicas=2 -n statefulset-demokubectl get pvc -n statefulset-demo -
Access individual pods via DNS from a debug pod:
Terminal window kubectl run -it debug --rm --image=busybox -n statefulset-demo -- \wget -qO- http://counter-sts-0.counter-sts:80
Cleanup
Section titled “Cleanup”kubectl delete namespace statefulset-demoNote: Deleting the namespace also deletes the PVCs. In production, scaling down a StatefulSet does NOT delete PVCs. You must clean them up manually.
Further Reading
Section titled “Further Reading”See docs/deep-dive.md for a detailed explanation of StatefulSet ordering guarantees, update strategies, PVC retention policies, headless service mechanics, and when to use StatefulSet vs Deployment vs DaemonSet.
Next Step
Section titled “Next Step”Move on to Jobs & CronJobs to learn about batch processing and scheduled tasks.