5.6 StatefulSet VolumeClaimTemplates & Persistent Storage Workflows
Key Takeaways
- StatefulSets provide stable, unique pod identities (pod-0, pod-1) and dedicated persistent storage per replica using volumeClaimTemplates, unlike Deployments where all replicas share the same volume.
- The dynamic PVC generation formula strictly follows '<volumeClaimTemplate-name>-<statefulset-name>-<ordinal-index>', creating an independent, zero-indexed PVC for every replica.
- When a StatefulSet Pod terminates or is rescheduled to a different node, the replacement Pod retains its exact ordinal identity and automatically reattaches to its dedicated existing PVC and PV.
- persistentVolumeClaimRetentionPolicy gives granular control over PVC lifecycle when a StatefulSet is deleted (whenDeleted: Retain/Delete) or scaled down (whenScaled: Retain/Delete).
- Partitioned rolling updates (updateStrategy.rollingUpdate.partition) enable phased canary deployments and controlled database schema migrations across ordinal stateful replicas.
5.6 StatefulSet VolumeClaimTemplates & Persistent Storage Workflows
Deploying distributed, stateful clustering systems—such as Apache Kafka, Elasticsearch, Cassandra, ZooKeeper, and replicated MySQL/PostgreSQL databases—presents challenges that standard Kubernetes Deployments and ReplicaSets cannot resolve. Stateless Deployments treat pods as anonymous, interchangeable entities: all replicas share the exact same PodSpec, scale up and down arbitrarily, and if configured with a PVC, attempt to mount the identical shared volume (requiring ReadWriteMany support).
Clustered stateful applications, however, demand three architectural invariants:
- Unique, persistent network identity that remains invariant across pod restarts.
- Dedicated, isolated persistent storage per replica that is never shared with peer replicas.
- Deterministic, ordered provisioning and graceful decommissioning to maintain distributed quorum.
Kubernetes fulfills these requirements through the StatefulSet workload controller.
1. StatefulSet vs. Deployment Storage Architecture
=============================== DEPLOYMENT STORAGE ===============================
Deployment (replicas: 3) ---> References single PVC: shared-pvc
+--------------+ +--------------+ +--------------+
| web-pod-7x8a | | web-pod-9b2c | | web-pod-1z4e |
+-------+------+ +-------+------+ +-------+------+
| | |
+-----------------+-----------------+
|
v
+-------------------+
| shared-pvc | (Requires RWX; all pods share same disk)
+-------------------+
============================== STATEFULSET STORAGE ===============================
StatefulSet: kafka (replicas: 3) with volumeClaimTemplate: data
+---------------+ +---------------+ +---------------+
| kafka-0 | | kafka-1 | | kafka-2 |
+-------+-------+ +-------+-------+ +-------+-------+
| | |
v v v
+---------------+ +---------------+ +---------------+
| data-kafka-0 | | data-kafka-1 | | data-kafka-2 |
| (PVC / PV) | | (PVC / PV) | | (PVC / PV) |
+---------------+ +---------------+ +---------------+
(Dedicated 1-to-1 persistent disk per ordinal replica; preserves state across failover)
| Architectural Dimension | Deployment Storage Model | StatefulSet Storage Model |
|---|---|---|
| Storage Declaration | References pre-existing PVC in spec.template.spec.volumes | Dynamically generates PVCs via spec.volumeClaimTemplates |
| Volume Cardinality | All replicas bind to the same single PVC | Each replica receives its own dedicated, isolated PVC |
| Pod Identity | Random hash suffix (e.g., api-6d4b9f7c-x8k2j) | Zero-indexed ordinal (e.g., kafka-0, kafka-1, kafka-2) |
| Failover Reattachment | Replacement pod binds to whatever volume is declared | Replacement kafka-1 binds strictly to existing data-kafka-1 |
| DNS Hostname | Ephemeral Pod IP behind Service | Predictable DNS via Headless Service |
2. Dynamic PVC Generation Formula
When a StatefulSet with replicas: N is applied, the StatefulSet controller dynamically synthesizes and creates $N$ individual PersistentVolumeClaim resources. The PVC names are generated using a deterministic formula:
<volumeClaimTemplate-name>-<statefulset-name>-<ordinal-index>
Formula Evaluation Matrix:
| StatefulSet Name | volumeClaimTemplates.metadata.name | Replica Count | Generated PVC Names |
|---|---|---|---|
cassandra | data | 3 | data-cassandra-0, data-cassandra-1, data-cassandra-2 |
zk-cluster | zookeeper-log | 3 | zookeeper-log-zk-cluster-0, zookeeper-log-zk-cluster-1, zookeeper-log-zk-cluster-2 |
elastic | storage | 2 | storage-elastic-0, storage-elastic-1 |
3. Pod Rescheduling & Persistent Storage Binding
The fundamental design guarantee of StatefulSets is that storage is bound to the ordinal identity, not the physical worker node.
+-----------------------------------------------------------------------------------------+
| STATEFULSET FAILOVER & REATTACHMENT |
| |
| [INITIAL HEALTHY STATE] |
| Worker Node 1: kafka-0 <=======> Mounts: data-kafka-0 (PV-0) |
| Worker Node 2: kafka-1 <=======> Mounts: data-kafka-1 (PV-1) |
| |
| [CRASH EVENT: Worker Node 2 Hardware Failure] |
| 1. Node 2 becomes NotReady -> Kubelet stops heartbeating. |
| 2. Node controller evicts kafka-1. |
| 3. StatefulSet controller detects missing replica with ordinal 1. |
| 4. Scheduler places new kafka-1 on Worker Node 3. |
| |
| [FAILOVER RECOVERY: Worker Node 3] |
| 5. Kubelet on Node 3 watches Pod kafka-1. |
| 6. CSI attacher detaches PV-1 from Node 2 and attaches PV-1 to Node 3. |
| 7. kafka-1 starts with 100% of its previous persistent data intact! |
+-----------------------------------------------------------------------------------------+
4. persistentVolumeClaimRetentionPolicy (StatefulSet Scaling & Deletion)
Historically, deleting or scaling down a StatefulSet never deleted any associated PVCs. While this protected against data loss, it caused orphaned PVCs and persistent cloud billing. Modern Kubernetes (stable since v1.32 and available in v1.35) provides the persistentVolumeClaimRetentionPolicy API.
spec:
persistentVolumeClaimRetentionPolicy:
whenDeleted: Retain # Options: Retain, Delete
whenScaled: Retain # Options: Retain, Delete
Retention Policies Explained:
whenDeleted: Dictates what occurs to associated PVCs when the parent StatefulSet resource is deleted (kubectl delete statefulset <name>):Retain(Default): PVCs and underlying PVs are preserved in the namespace.Delete: All PVCs created byvolumeClaimTemplatesare automatically deleted along with the StatefulSet.
whenScaled: Dictates what occurs to PVCs when the StatefulSet replica count is reduced (e.g., scaling from 5 replicas down to 2):Retain(Default): PVCs for scaled-down pods (data-app-4,data-app-3,data-app-2) remain intact. If scaled back up, the pods reattach to them.Delete: The PVCs belonging to the terminated replicas are permanently deleted, freeing cloud storage assets.
5. Pod Management Policies & Headless Services
1. podManagementPolicy (OrderedReady vs. Parallel)
OrderedReady(Default): Replicas are created sequentially in strict ascending order ($0 \to 1 \to 2$). Pod $N$ is not launched until Pod $N-1$ is in theRunningandReadystate. Teardown occurs in reverse order ($2 \to 1 \to 0$). Essential for clustering protocols (e.g., bootstrapping Raft leaders).Parallel: Replicas are launched and terminated concurrently without waiting for peer readiness. Used when workloads need independent storage identities but rapid scaling.
2. Headless Service (clusterIP: None)
A StatefulSet requires a companion Headless Service to establish stable network identities. CoreDNS automatically generates A/AAAA DNS records for each individual pod:
<pod-name>.<service-name>.<namespace>.svc.cluster.local
Example: kafka-0.kafka-headless.production.svc.cluster.local.
6. Partitioned Rolling Updates & Canary Migrations
StatefulSets support staged rollouts using the partition directive under spec.updateStrategy.rollingUpdate.
spec:
updateStrategy:
type: RollingUpdate
rollingUpdate:
partition: 2
Partition Mechanics & Canary Progression
When partition: 2 is specified on a 4-replica StatefulSet (db-0, db-1, db-2, db-3):
- Only replicas with ordinal index $\ge 2$ (
db-2anddb-3) are updated to the new PodSpec. - Replicas with ordinal index $< 2$ (
db-0anddb-1) remain untouched on the legacy version. - Canary Database Migration Workflow:
- Set
partition: 3-> Onlydb-3is updated. Validate telemetry, database schema migrations, and replication lag. - If healthy, decrement
partition: 1-> Updatesdb-2anddb-1. - Decrement
partition: 0-> Updates the final replica (db-0), completing full cluster upgrade.
- Set
7. Production-Grade StatefulSet Declarative Manifest
apiVersion: v1
kind: Service
metadata:
name: postgres-ha
namespace: production
labels:
app: postgres
spec:
clusterIP: None
ports:
- port: 5432
name: postgresql
selector:
app: postgres
---
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: pg-cluster
namespace: production
spec:
serviceName: postgres-ha
replicas: 3
podManagementPolicy: OrderedReady
updateStrategy:
type: RollingUpdate
rollingUpdate:
partition: 0
persistentVolumeClaimRetentionPolicy:
whenDeleted: Retain
whenScaled: Retain
selector:
matchLabels:
app: postgres
template:
metadata:
labels:
app: postgres
spec:
containers:
- name: postgres
image: postgres:16-alpine
ports:
- containerPort: 5432
name: postgresql
volumeMounts:
- name: pgdata
mountPath: /var/lib/postgresql/data
subPath: data
volumeClaimTemplates:
- metadata:
name: pgdata
spec:
accessModes: [ "ReadWriteOnce" ]
storageClassName: high-performance-ebs
resources:
requests:
storage: 100Gi
8. StatefulSet Disaster Recovery & Storage Maintenance
# 1. Scale down StatefulSet to isolate broken replica
kubectl scale statefulset pg-cluster -n production --replicas=2
# 2. Inspect orphaned PVCs left behind after scale-down
kubectl get pvc -n production -l app=postgres
# 3. Perform online volume expansion on StatefulSet PVCs
kubectl patch pvc pgdata-pg-cluster-0 -n production --type=merge -p '{"spec":{"resources":{"requests":{"storage":"200Gi"}}}}'
kubectl patch pvc pgdata-pg-cluster-1 -n production --type=merge -p '{"spec":{"resources":{"requests":{"storage":"200Gi"}}}}'
kubectl patch pvc pgdata-pg-cluster-2 -n production --type=merge -p '{"spec":{"resources":{"requests":{"storage":"200Gi"}}}}'
A StatefulSet named kafka-cluster with replicas: 4 defines a volumeClaimTemplate with metadata.name: datadir. What is the exact name of the PersistentVolumeClaim dynamically generated for the third replica in this set?
An administrator scales down a StatefulSet named redis-cluster from 5 replicas down to 2 replicas under default retention settings. What happens to the PVCs associated with replicas 2, 3, and 4?
You need to perform a canary deployment of a new database engine version on a 4-replica StatefulSet (pg-0 through pg-3), updating only pg-3 while keeping pg-0, pg-1, and pg-2 on the existing version. Which updateStrategy configuration achieves this?