StatefulSets Basics
This page introduces the StatefulSet controller through small, runnable examples: stable pod names, per-pod storage, ordered rollout, and safe scaling.
Work through the basic examples first, then the intermediate ones for update and retention control.
Prerequisites
- A Kubernetes 1.36.2 cluster (containerd via CRI on nodes) and
kubectlmatching your server minor. - A default StorageClass with dynamic provisioning, or a named class you can reference in
volumeClaimTemplates. - Helm 3 and Compose v2 are useful elsewhere in this cookbook but are not required here.
- Confirm access before starting.
kubectl version
kubectl get storageclassBasic Examples
1. A minimal StatefulSet
The smallest StatefulSet needs a serviceName, a selector, and a pod template.
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: web
spec:
serviceName: web
replicas: 3
selector:
matchLabels:
app: web
template:
metadata:
labels:
app: web
spec:
containers:
- name: nginx
image: nginx:1.27
ports:
- containerPort: 80serviceNamepoints at the headless Service that backs stable DNS.- The selector must match the pod template labels exactly.
- Pods are created as
web-0,web-1,web-2in order. - Each name is stable across rescheduling.
2. The headless Service it needs
A StatefulSet relies on a Service with clusterIP: None to publish per-pod DNS.
apiVersion: v1
kind: Service
metadata:
name: web
spec:
clusterIP: None
selector:
app: web
ports:
- port: 80clusterIP: Nonemakes the Service headless.- DNS then resolves
web-0.web,web-1.web, and so on. - The Service name must equal the StatefulSet
serviceName. - Apply this before or alongside the StatefulSet.
3. Observing ordered creation
Apply both manifests and watch pods come up one at a time.
kubectl apply -f service.yaml -f statefulset.yaml
kubectl get pods -l app=web -wweb-1will not start untilweb-0is Ready.- The
-wflag streams changes as they happen. - Ordinals always start at zero.
- This ordering is the default
OrderedReadypolicy.
4. Per-pod storage with volumeClaimTemplates
Add a volumeClaimTemplates block to give each pod its own volume.
volumeClaimTemplates:
- metadata:
name: data
spec:
accessModes: ["ReadWriteOnce"]
storageClassName: standard
resources:
requests:
storage: 1Gi- Kubernetes creates one PVC per pod:
data-web-0,data-web-1, and so on. - A pod always re-attaches to its own claim after rescheduling.
- Mount it in the container via
volumeMountswithname: data. - Set
storageClassNameexplicitly rather than relying on the default.
Mount the claim inside the pod template so the data is actually used.
containers:
- name: nginx
image: nginx:1.27
volumeMounts:
- name: data
mountPath: /usr/share/nginx/html- The
namemust match thevolumeClaimTemplatesentry. - Each pod sees only its own volume at that path.
- The mount survives pod restarts and reschedules.
- No shared
volumesentry is needed; the template provides it.
5. Reaching one specific pod by DNS
Each pod is individually addressable through the headless Service.
kubectl run tmp --rm -it --image=busybox:1.36 -- \
nslookup web-0.web.default.svc.cluster.local- The record format is
<pod>.<service>.<namespace>.svc.cluster.local. - This lets peers target a named member, not a random replica.
- The A record follows the ordinal, not the node.
- Deployments do not offer per-pod DNS like this.
6. Scaling up and down
Scale a StatefulSet with the same command you use for a Deployment.
kubectl scale statefulset/web --replicas=5- Scaling up creates
web-3thenweb-4in order. - Scaling down removes the highest ordinals first:
web-4, thenweb-3. - PVCs from removed pods are kept by default.
- Never edit ordinals by hand; let the controller manage them.
7. Inspecting a StatefulSet
Standard kubectl verbs work for status and troubleshooting.
kubectl get statefulset web
kubectl describe statefulset web
kubectl get pvc -l app=webgetshows desired versus ready replicas.describesurfaces events like failed scheduling or volume binding.- Listing PVCs confirms each pod got its own storage.
- Ready count lags during ordered rollout by design.
Intermediate Examples
8. Rolling updates with a partition
The default update strategy is RollingUpdate, and a partition lets you canary high ordinals first.
updateStrategy:
type: RollingUpdate
rollingUpdate:
partition: 2- Only pods with an ordinal at or above
2are updated. - Lower the partition step by step to advance the rollout.
- Set it back to
0to update every pod. - This gives you a controlled canary on a quorum system.
9. Parallel pod management
When your app tolerates simultaneous starts, skip the ordered wait.
spec:
podManagementPolicy: Parallel- Pods are created and deleted all at once instead of in sequence.
- This speeds up scaling for shared-nothing workloads.
- It does not change update ordering, only create and delete.
- Leave it at the default
OrderedReadyfor quorum databases.
10. Controlling PVC retention
Decide what happens to per-pod volumes on scale-down or deletion.
spec:
persistentVolumeClaimRetentionPolicy:
whenScaled: Delete
whenDeleted: RetainwhenScaled: Deletereclaims PVCs when you scale in.whenDeleted: Retainkeeps data if the StatefulSet itself is removed.- The default is
Retainfor both, which preserves data but can leak storage. - Choose deliberately so you neither lose data nor pay for orphans.
11. Ordering shutdown gracefully
StatefulSet pods terminate in reverse ordinal order, and you can give each one time to leave cleanly.
spec:
template:
spec:
terminationGracePeriodSeconds: 60- Highest ordinal terminates first:
web-2, thenweb-1, thenweb-0. - A generous grace period lets a database flush and deregister.
- Pair it with a preStop hook for clustered systems.
- The default of 30 seconds is often too short for stateful apps.
Stack versions: This page was written for Kubernetes 1.36.2, Docker Engine 29.6.1 (BuildKit default), containerd (CRI runtime on nodes), Helm 3, Compose v2, Argo CD (latest - verify at build), and Gateway API (GA - verify controller support at build).