StatefulSets Explained
Introduction¶
A Deployment treats pods as interchangeable cattle. A StatefulSet gives each pod a stable identity that survives rescheduling — a persistent name, a persistent volume, and a predictable order. That's what clustered databases, message queues, and consensus systems need.
The guarantees¶
Stable network identity¶
Pods are named <statefulset>-0, <statefulset>-1, … (the ordinal). Pod
db-1 is always db-1, even after it's deleted and recreated on another node.
With a headless Service (clusterIP: None), each pod gets a stable DNS name:
db-0.db.team-a.svc.cluster.local
db-1.db.team-a.svc.cluster.local
so db-1 can always find db-0 (e.g. to replicate from it). The headless
Service is required — the StatefulSet's serviceName field must point at it.
Stable storage¶
volumeClaimTemplates creates one PVC per pod: data-db-0, data-db-1. When
db-1 is rescheduled, it re-attaches the same PVC. Deleting the StatefulSet
does not delete the PVCs (by default) — your data is safe from a
kubectl delete.
Ordered operations¶
- Scale up:
db-0, thendb-1, thendb-2— each waiting for the previous to be Running and Ready. - Scale down: reverse order,
db-2first. - Rolling update: highest ordinal first, down to 0, one at a time, waiting for Ready.
podManagementPolicy: Parallel disables the ordering for start/stop (not
updates) if your app doesn't need it.
A StatefulSet¶
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: db
spec:
serviceName: db # the headless Service
replicas: 3
selector:
matchLabels: { app: db }
template:
metadata:
labels: { app: db }
spec:
containers:
- name: postgres
image: postgres:16
ports: [{ containerPort: 5432, name: pg }]
volumeMounts:
- name: data
mountPath: /var/lib/postgresql/data
volumeClaimTemplates:
- metadata: { name: data }
spec:
accessModes: [ReadWriteOnce]
storageClassName: fast
resources: { requests: { storage: 100Gi } }
---
apiVersion: v1
kind: Service
metadata: { name: db }
spec:
clusterIP: None
selector: { app: db }
ports: [{ port: 5432, name: pg }]
Partitioned rollouts (canary)¶
updateStrategy:
type: RollingUpdate
rollingUpdate:
partition: 2 # only pods with ordinal >= 2 update
Set partition: 2 on a 3-replica set → only db-2 gets the new version.
Verify it, then lower to 0 to roll the rest. A manual canary for stateful
systems.
When NOT to use a StatefulSet¶
- Your app is actually stateless — use a Deployment; StatefulSet's ordering just slows deploys.
- You need shared (RWX) storage across all pods — that's a Deployment + an RWX PVC, not per-pod volumes.
- The "state" is external (RDS, managed Kafka) — the app tier is stateless.
- You want fast parallel scaling and pods don't need identity — Deployment.
Verification and troubleshooting¶
kubectl get statefulset db
kubectl get pods -l app=db -o wide # -0, -1, -2 in order
kubectl get pvc -l app=db
kubectl get svc db # CLUSTER-IP should be None
kubectl exec db-1 -- nslookup db-0.db
- Pods stuck at
db-0Pending, rest never start — ordered startup:db-0isn't Ready (failing readiness probe, PVC unbound, image pull). Fixdb-0first.ParallelpodManagementPolicy if ordering isn't needed. db-1can't resolvedb-0.db—serviceNamedoesn't match the headless Service name, the Service isn't headless (clusterIP: None), or it has apublishNotReadyAddressesneed (peers must find each other before Ready — setpublishNotReadyAddresses: trueon the Service).- PVC stuck
Pending— StorageClass issues (see Persistent Volumes and Claims).WaitForFirstConsumeris normal until the pod schedules. - Scaled down then up, data gone — you deleted the PVCs, or the new pod
landed in a different zone and couldn't attach the zonal volume. PVCs persist
by default; check
kubectl get pvc. - Update stuck —
partitionis set above 0, or a pod won't become Ready so the rollout won't proceed to the next ordinal.kubectl rollout status statefulset/db. kubectl delete statefulsetleft PVCs and pods —--cascade=orphanwas used, or PVCs are retained by design. Delete PVCs explicitly if you mean to.- Multi-Attach error after a node crash — the old pod's RWO volume is still attached to the dead node; wait for the timeout or force-delete the old pod.
Related tools and reading¶
- On-site: YAML ↔ JSON converter.
- Related posts: Persistent Volumes and Claims, Kubernetes Services and Endpoints.
Stuck on something this site can't fix?Reach out to Prabath directly on LinkedIn.More in Docker & Kubernetes
Persistent Volumes and Claims, Explained
PVC is a request, PV is the fulfilled storage, StorageClass is the factory. Most 'Pending PVC' problems are an access-mode or provisioner mismatch.
October 24, 2026Kubernetes Jobs and CronJobs
A Job runs pods until they succeed; a CronJob creates Jobs on a schedule. Here's parallelism, retry limits, the missed-schedule trap, and history cleanup.
October 24, 2026Container Networking: bridge, host, none, and Custom Networks
The four network modes, why you should almost always use a user-defined bridge, and what -p actually does to your firewall.
October 24, 2026