Learn how Kubernetes StatefulSets work under the hood, from identity and ordering to updates and failure recovery, with practical examples and verification steps.
Intro
Kubernetes StatefulSets are the go-to workload controller for applications that require stable identity, persistent storage, and ordered deployment and scaling. While many engineers are familiar with the basics, advanced concepts such as identity mechanics, update strategies, partition rolling updates, and forced rollback are often misunderstood. This article provides a practical deep dive into these advanced concepts, explaining how StatefulSets work internally and how to apply them safely in production. By the end, you will have a clear understanding of when these features matter and how to verify their behavior with hands-on examples. We will cover prerequisites, safe configuration, verification techniques, failure modes, recovery, and an operations checklist.
Version and Environment Inventory
Before diving into advanced StatefulSet concepts, establish a controlled environment. The examples in this article assume a Kubernetes cluster running version 1.24 or later, as features like the --field-manager flag and stable CSI drivers are mature. You need kubectl configured with cluster-admin permissions in a development namespace. A storage class that supports dynamic provisioning is required for persistent volume claims. For local experimentation, tools like Kind or Minikube with a default storage provisioner work well. The table below summarizes the environment prerequisites.
| Component | Recommended Version | Purpose |
|---|---|---|
| Kubernetes | >= 1.24 | StatefulSet features and API stability |
| kubectl | >= 1.24 | CLI interaction |
| StorageClass | Dynamic provisioner | For PVC creation |
| Namespace | dedicated dev namespace | Isolation |
Verify your cluster version and storage class with the following commands. The expected output shows the server version and an available storage class with (default) marker.
$ kubectl version --short
Client Version: v1.24.0
Server Version: v1.24.0
$ kubectl get storageclass
NAME PROVISIONER RECLAIMPOLICY VOLUMEBINDINGMODE ALLOWVOLUMEEXPANSION AGE
standard (default) rancher.io/local-path Delete WaitForFirstConsumer false 10d
Safe Configuration Path
Advanced StatefulSet configurations often involve tweaking update strategies and partition parameters. To minimize risk, apply changes incrementally and observe behavior. The most important advanced fields are spec.podManagementPolicy, spec.updateStrategy, and spec.replicas.
podManagementPolicy can be OrderedReady (default) or Parallel. The default ensures pods are created and deleted in order, waiting for each to become ready before proceeding. Parallel allows simultaneous pod operations, useful for stateless-like scaling of stateful applications that manage their own coordination.
updateStrategy controls how rolling updates are performed. The rollingUpdate type has a partition field. If specified, only pods with an ordinal greater than or equal to the partition are updated. All pods with an ordinal less than the partition remain at the old version. This enables canary-style updates for stateful applications.
Here is an example StatefulSet manifest with a partition-based rolling update:
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: web
spec:
serviceName: "nginx"
replicas: 3
selector:
matchLabels:
app: nginx
updateStrategy:
type: RollingUpdate
rollingUpdate:
partition: 2
podManagementPolicy: OrderedReady
template:
metadata:
labels:
app: nginx
spec:
containers:
- name: nginx
image: nginx:1.24
ports:
- containerPort: 80
volumeMounts:
- name: www
mountPath: /usr/share/nginx/html
volumeClaimTemplates:
- metadata:
name: www
spec:
accessModes: [ "ReadWriteOnce" ]
storageClassName: "standard"
resources:
requests:
storage: 1Gi
Apply this manifest with kubectl apply -f statefulset.yaml. The StatefulSet creates three pods named web-0, web-1, web-2 in sequential order. Because the partition is 2, if you later change the image to nginx:1.25, only web-2 will be updated while web-0 and web-1 remain at the old version. This is a safe way to test an upgrade on a single pod before rolling out to all.
Understanding Pod Identity and Stable Network Identity
A core advanced concept is the stable identity that StatefulSets provide. Each pod gets a predictable name based on the StatefulSet name and an ordinal index: web-0, web-1, web-2. These names are sticky: if a pod dies, its replacement gets the same name. This identity is tied to the pod's persistent volume claims and stable network identity via a headless service.
Define a headless service for the StatefulSet to enable stable DNS entries:
apiVersion: v1
kind: Service
metadata:
name: nginx
labels:
app: nginx
spec:
clusterIP: None
selector:
app: nginx
ports:
- port: 80
name: web
With this service, each pod gets a DNS name like web-0.nginx.default.svc.cluster.local. This stable network identity is critical for stateful applications that need to address peers by a consistent name, such as database replicas or clustered message brokers.
Volume Claim Templates and Persistent Storage
The volumeClaimTemplates field defines a template for creating a PersistentVolumeClaim (PVC) for each pod. When a pod is created, the StatefulSet controller creates a PVC named www-web-0, www-web-1, etc. These PVCs are not deleted when a pod is deleted; they persist to preserve data. If a pod is rescheduled to another node, it reattaches to the same PVC, ensuring data continuity.
Verification and Diagnostics
After applying configuration changes, verify that the StatefulSet behaves as expected. Use kubectl get statefulset to view status, kubectl describe statefulset for events, and kubectl get pods with labels to inspect individual pods.
To verify the partition update behavior, first check the current image of each pod with the following command. The output will show which pods are still on the old image and which have been updated.
$ kubectl get pods -l app=nginx -o jsonpath='{range .items[*]}{.metadata.name}{"\t"}{.spec.containers[0].image}{"\n"}{end}'
web-0 nginx:1.24
web-1 nginx:1.24
web-2 nginx:1.25
This confirms that only pod web-2 was updated due to the partition. To observe the ordering of pod creation after a scale-down and scale-up, delete the StatefulSet pods and watch them recreate. For example, if you delete web-1, the controller will recreate it only after web-0 is ready, if podManagementPolicy is OrderedReady.
Run the following command to watch pod events:
kubectl get pods -l app=nginx -w
You will see web-0 become ready before web-1 is created. This ordered behavior is essential for applications that require a specific startup sequence.
For storage verification, check that each pod has a unique persistent volume claim and that the volume is bound:
$ kubectl get pvc -l app=nginx
NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS AGE
www-web-0 Bound pvc-1234abcd-56ef-7890-ghij-klmnopqrstuv 1Gi RWO standard 5m
www-web-1 Bound pvc-5678efgh-90ij-klmn-opqr-stuvwxyzabcd 1Gi RWO standard 5m
www-web-2 Bound pvc-9012ijkl-mnop-qrst-uvwx-yzabcdefghij 1Gi RWO standard 5m
This shows that each pod has its own persistent volume, a key StatefulSet advantage over Deployments.
Inspecting StatefulSet Status
Use kubectl get statefulset web -o yaml to inspect the status fields. Key fields include:
replicas: desired number of podsreadyReplicas: number of pods that are readycurrentReplicas: number of pods at the current versionupdatedReplicas: number of pods at the updated version
For example:
$ kubectl get statefulset web
NAME READY AGE
web 3/3 10m
If you see a discrepancy between replicas and readyReplicas, investigate pod readiness probes and logs.
Failure Modes and Recovery
StatefulSets can fail in several ways. Common failure modes include pods stuck in Pending due to PVC binding issues, pods crashing because of application misconfiguration, or update failures causing rollback needs. Because StatefulSets maintain identity, deleting a pod does not remove its PVC; the data persists. However, if you delete the StatefulSet itself, the PVCs remain by default, which may be desired for data retention but can cause issues if you recreate the StatefulSet with the same name and expect it to reuse old volumes. The StatefulSet controller does not automatically adopt existing PVCs unless they match the naming pattern and labels.
For forced rollback after a failed update, you can use kubectl rollout undo statefulset web. This reverts to the previous revision stored in the controller's history. However, note that StatefulSet rollouts have limited history (10 by default). To manually roll back a partition update, simply lower the partition to 0, which will update all pods to the target version. If the target version is broken, you must change the image back to the previous version and apply again.
Another failure mode is when a pod becomes unready and blocks the ordered update. If a pod with ordinal lower than the partition is not ready, the controller will not proceed to update higher ordinals. To resolve, you may need to delete the offending pod or fix its readiness.
Diagnosing a Stuck Pod
To recover from a stuck StatefulSet, first inspect events:
$ kubectl describe statefulset web
...
Events:
Type Reason Age From Message
---- ------ ---- ---- -------
Warning FailedCreate 10m statefulset-controller create Pod web-0 in StatefulSet web failed error: pods "web-0" is forbidden: unable to validate against any pod security policy
This example shows a pod security policy issue. Fix the policy or adjust the pod security context. After fixing, the controller will retry automatically.
For storage-related failures, ensure the storage class is available and the PVC is bound. If a PVC is stuck in Pending, check the storage class events:
kubectl describe pvc www-web-0
Look for events indicating provisioning failures. You may need to delete and recreate the PVC or adjust the storage class.
Manual Intervention for Orphaned PVCs
If you delete a StatefulSet but keep the PVCs, then recreate the StatefulSet with the same name, the controller will not automatically adopt the existing PVCs because they lack the correct labels. You must manually label them or pre-provision them. To adopt an existing PVC, ensure its name matches www-web-<ordinal> and it has the label app=nginx. If not, you can label it:
kubectl label pvc www-web-0 app=nginx
Then recreate the StatefulSet, and the controller will bind the existing PVC to the pod.
Forcing Pod Deletion with --force and --grace-period=0
If a pod is stuck in Terminating state, you can force delete it:
kubectl delete pod web-0 --force --grace-period=0
This should be used as a last resort, as it bypasses graceful shutdown and may lead to data corruption if the application does not handle abrupt termination.
Operations Checklist
Use this checklist when operating StatefulSets in production to ensure stability and quick recovery.
| Step | Action | Verification |
|---|---|---|
| 1 | Before any update, record current image versions and configuration | kubectl get statefulset web -o yaml > backup.yaml |
| 2 | Set update strategy partition to test on a canary pod | kubectl patch statefulset web -p '{"spec":{"updateStrategy":{"rollingUpdate":{"partition":2}}}}' |
| 3 | Monitor pod status and logs during update | kubectl get pods -w and kubectl logs web-2 |
| 4 | If canary is healthy, lower partition to 0 for full rollout | kubectl patch statefulset web -p '{"spec":{"updateStrategy":{"rollingUpdate":{"partition":0}}}}' |
| 5 | Verify all pods are ready and on new version | kubectl get pods -l app=nginx -o jsonpath='{range .items[*]}{.metadata.name}{"\t"}{.spec.containers[0].image}{"\n"}{end}' |
| 6 | Check PVCs are intact and bound | kubectl get pvc -l app=nginx |
| 7 | If rollback needed, use undo or revert image | kubectl rollout undo statefulset web or edit manifest |
| 8 | Document any manual interventions in runbook | Update operational notes |
Additionally, regularly review the StatefulSet's status fields (currentReplicas, updatedReplicas, readyReplicas) to catch discrepancies early.
Example Canary Update Workflow
Here is a concrete canary update workflow for the web StatefulSet:
- Baseline: All pods on
nginx:1.24, partition=3 (default, no updates). - Canary: Patch partition to 2 and update image to
nginx:1.25. Onlyweb-2updates. - Validate: Check logs, run health checks against
web-2. - Rollout: Patch partition to 0.
web-1andweb-0update in descending order (since partition 0 means all pods with ordinal >= 0 are updated, but the controller still respects ordered policy). - Confirm: All pods report
nginx:1.25and are ready.
If any step fails, you can roll back by setting the image back to nginx:1.24 and adjusting the partition appropriately.
Advanced: Scaling and Deletion Order
When scaling down a StatefulSet, pods are deleted in reverse ordinal order: the highest ordinal is deleted first. This ensures that the last pod to be deleted is the one that likely holds the most critical data or is the primary in a cluster. For example, scaling from 3 to 2 deletes web-2 first, then web-1 if scaling further.
When deleting the StatefulSet, you can use --cascade=orphan to keep the pods running while deleting the controller. This is useful for maintenance or migration.
kubectl delete statefulset web --cascade=orphan
The pods remain but are no longer managed by the StatefulSet. You can later adopt them by recreating the StatefulSet with matching labels.
Conclusion
Advanced StatefulSet concepts, particularly identity, ordering, and partition-based updates, give you fine-grained control over stateful applications. By understanding these internals, you can safely deploy and manage databases, message queues, and other stateful services on Kubernetes. We covered environment prerequisites, safe configuration with partition updates, verification through detailed commands, failure modes with recovery steps, and a practical operations checklist. Start by applying these techniques in a development cluster with a simple StatefulSet, then progress to production with confidence. Remember to always test changes with a partition and maintain backups of your StatefulSet manifests and persistent data.