Kubernetes StatefulSet Generator
Build a StatefulSet with its volume claim template and the headless Service reference it needs. The parts that are immutable, and the volumes that are never deleted, are called out beside the output.
statefulset.yaml
updates as you type Common mistakes
These are the ones that fail silently. The config is accepted, nothing raises an error, and the consequence arrives later.
Expecting volumeClaimTemplates to be cleaned up
PVCs created by a StatefulSet survive scaling down and deleting the StatefulSet itself. They persist deliberately, and they keep costing.
Instead:Delete them explicitly when you mean to, and know the retention policy fields in newer versions.
Omitting the headless Service
serviceName must name a headless Service, and without it the stable per-pod DNS names do not exist.
Instead:Create the headless Service first and reference it.
Assuming ordered startup means ordered readiness downstream
Pods start in order and each waits for the previous to be Ready, which makes rollouts slow and a stuck pod block everything behind it.
Instead:Use Parallel pod management where ordering is not actually required.
What a StatefulSet gives you that a Deployment does not
Stable identity. Each pod gets an ordinal that survives rescheduling, a DNS name that goes with it, and its own volume that follows it. That is the whole feature, and each part of it has a cost that is not obvious until it bites.
The headless Service is not optional, and nothing creates it
spec.serviceName names a Service that must already exist with clusterIP: None. Nothing validates the reference. Get it wrong and the pods still start and look healthy, but the per-pod DNS names never resolve, which is the entire reason to use a StatefulSet rather than a Deployment.
apiVersion: v1
kind: Service
metadata:
name: api-headless
spec:
clusterIP: None <- headless. Required.
selector:
app.kubernetes.io/name: api
ports:
- port: 8080
# then each pod resolves at:
# api-0.api-headless.default.svc.cluster.local The volumes outlive everything, on purpose
volumeClaimTemplates creates one PVC per replica. Scaling down does not delete them. Deleting the StatefulSet does not delete them. That is the safe default, because the data is the point, and it is a recurring surprise on the cloud bill months later.
scale 3 -> 10 creates data-api-3 .. data-api-9
scale 10 -> 3 pods go, seven PVCs stay, still billed
scale 3 -> 10 the old PVCs are reattached, with
their old data
kubectl delete sts api PVCs still there
# 1.27+ has persistentVolumeClaimRetentionPolicy to
# opt into deletion. It is not the default. OrderedReady means one bad pod blocks everything after it
By default pod N+1 is not created until pod N is Ready. A pod that can never become Ready therefore stops the entire set from progressing, indefinitely, and scaling up appears to do nothing at all. Parallel starts them all at once and is right whenever the workload does not need ordered startup.
podManagementPolicy: OrderedReady (default)
api-0 Ready -> create api-1 -> Ready -> api-2 ...
api-1 CrashLoopBackOff -> api-2 never created
podManagementPolicy: Parallel
all pods created at once
# this field is immutable after creation. Rolling updates go backwards, and partition is the canary
Updates proceed from the highest ordinal down to zero, one pod at a time, waiting for each to become Ready. partition stops that descent: only pods at or above the partition are updated. Lower it gradually to roll out, and a forgotten partition looks exactly like a rollout that stalled halfway.
replicas: 5, partition: 4
only api-4 is updated. api-0 to api-3 keep the
old spec indefinitely.
partition: 0 the full rollout, 4 then 3 then 2...
# nothing warns that a partition is still set. The volume size is immutable in the template
Changing storage in volumeClaimTemplates on an existing StatefulSet is rejected. Growing the volumes means editing each PVC directly, if the StorageClass has allowVolumeExpansion. Shrinking is never possible. And with a StorageClass using Immediate binding, a volume can be created in a zone the pod cannot reach, leaving it Pending forever.
# growing, one PVC at a time:
kubectl patch pvc data-api-0 -p \
'{"spec":{"resources":{"requests":{"storage":"20Gi"}}}}'
# check first:
kubectl get storageclass -o custom-columns=\
NAME:.metadata.name,EXPAND:.allowVolumeExpansion,\
BINDING:.volumeBindingMode