Stage 5 · Platform
Stateful Storage Operations
StatefulSet Volumes
volumeClaimTemplates, stable identities, ordered rollouts, and persistent replica data.
StatefulSet Storage Model
StatefulSets provide stable network identities and persistent storage for stateful applications. Each pod gets a unique, sticky identity (pod-0, pod-1, pod-2) and a persistent volume that survives pod restarts, reschedules, and resizes.
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: postgres
spec:
serviceName: postgres
replicas: 3
podManagementPolicy: OrderedReady
updateStrategy:
type: RollingUpdate
selector:
matchLabels:
app: postgres
template:
metadata:
labels:
app: postgres
spec:
containers:
- name: postgres
image: postgres:16
ports:
- containerPort: 5432
env:
- name: POSTGRES_PASSWORD
valueFrom:
secretKeyRef:
name: postgres-credentials
key: password
volumeMounts:
- name: data
mountPath: /var/lib/postgresql/data
resources:
requests:
cpu: "500m"
memory: "1Gi"
volumeClaimTemplates:
- metadata:
name: data
spec:
accessModes: ["ReadWriteOnce"]
storageClassName: fast-ssd
resources:
requests:
storage: 100GivolumeClaimTemplates creates a PVC for each pod. Pods get data-postgres-0, data-postgres-1, data-postgres-2. These PVCs persist across pod restarts. OrderedReady ensures pods are created and updated in order.
volumeClaimTemplates
volumeClaimTemplates defines the PVC template for each pod. Each pod gets its own PVC with a unique name. PVCs are NOT deleted when the StatefulSet is deleted — you must manually clean up data.
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: kafka
spec:
serviceName: kafka
replicas: 3
volumeClaimTemplates:
- metadata:
name: data
spec:
accessModes: ["ReadWriteOnce"]
storageClassName: fast-ssd
resources:
requests:
storage: 500Gi
- metadata:
name: logs
spec:
accessModes: ["ReadWriteOnce"]
storageClassName: standard
resources:
requests:
storage: 50GiKafka needs two volumes: data (for logs and metadata) and logs (for application logs). Each pod gets data-kafka-0 and logs-kafka-0. Different StorageClasses provide different performance tiers.
Stable Identities
StatefulSet pods get predictable DNS names: pod-name.service-name.namespace.svc.cluster.local. This is used by applications for peer discovery (e.g., Kafka brokers finding each other). The identity is stable across pod restarts.
apiVersion: v1
kind: Service
metadata:
name: postgres
spec:
clusterIP: None # Headless
selector:
app: postgres
ports:
- port: 5432
targetPort: 5432The headless service (clusterIP: None) returns individual pod IPs. Each pod gets a DNS record: postgres-0.postgres.default.svc.cluster.local. This is required for StatefulSet peer discovery.
Ordered Rollouts
StatefulSets update pods in reverse order: pod-2, pod-1, pod-0. This ensures ordered startup and shutdown. podManagementPolicy controls whether pods are created/updated in parallel (Parallel) or sequential (OrderedReady).
Scaling up:
pod-0 created -> pod-0 ready -> pod-1 created -> pod-1 ready -> pod-2 created -> pod-2 ready
Rolling update:
pod-2 updated and ready -> pod-1 updated and ready -> pod-0 updated and ready
Scaling down:
pod-2 deleted -> pod-1 deleted -> pod-0 deleted
Parallel (faster but less safe):
All pods created/updated simultaneouslyOrderedReady ensures each pod is ready before the next is created. This is critical for stateful applications that need specific startup order. Parallel is faster but risks data corruption if pods start simultaneously.
Data Persistence
StatefulSet PVCs persist across pod restarts, reschedules, and even node failures. However, PVCs are NOT deleted when the StatefulSet is deleted. This prevents accidental data loss but requires manual cleanup.
# List StatefulSet PVCs
kubectl get pvc -l app=postgres
# Delete StatefulSet (PVCs remain)
kubectl delete statefulset postgres
# Manually delete PVCs (data is lost)
kubectl delete pvc data-postgres-0 data-postgres-1 data-postgres-2
# Or use a Helm hook to clean up
# helm.sh/resource-policy: deleteWhen you delete a StatefulSet, the pods are deleted but the PVCs remain. This is intentional — it prevents accidental data loss. To delete data, explicitly delete the PVCs. The underlying volume is deleted based on the reclaim policy.
Scaling StatefulSets
Scaling a StatefulSet adds or removes pods in order. Scaling up adds pods sequentially (pod-3, pod-4, ...). Scaling down removes the highest-numbered pod first. Each new pod gets a new PVC from the volumeClaimTemplate.
# Scale up from 3 to 5 replicas
kubectl scale statefulset postgres --replicas=5
# Watch pod creation
kubectl get pods -w -l app=postgres
# Verify new PVCs created
kubectl get pvc -l app=postgresScaling up creates pod-3 and pod-4 with new PVCs (data-postgres-3, data-postgres-4). Scaling down deletes the highest-numbered pods first. PVCs are not deleted during scale-down.
Use Helm resource policies to control PVC lifecycle: helm.sh/resource-policy: keep (preserve on upgrade/delete) or helm.sh/resource-policy: delete (clean up on delete). This automates data management.
Mark this lesson complete to store local progress and unlock a cleaner resume path the next time you visit.