StatefulSets
A StatefulSet manages stateful apps that need stable identities and persistent storage per Pod.
Deployments treat Pods as interchangeable. But databases and other stateful apps need each Pod to keep its identity and its own disk. That is the job of a StatefulSet.
What it guarantees
- Stable names — Pods are named predictably:
db-0,db-1,db-2. - Stable storage — each Pod gets its own PersistentVolume that survives restarts.
- Ordered operations — Pods start and stop one at a time, in order.
Headless Service
StatefulSets pair with a headless Service (clusterIP: None) that gives each Pod a stable DNS name like db-0.db.default.svc.cluster.local, so peers can find one another.
Example
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: db
spec:
serviceName: db
replicas: 3
selector:
matchLabels:
app: db
template:
metadata:
labels:
app: db
spec:
containers:
- name: postgres
image: postgres:16
volumeMounts:
- name: data
mountPath: /var/lib/postgresql/data
volumeClaimTemplates:
- metadata:
name: data
spec:
accessModes: ["ReadWriteOnce"]
resources:
requests:
storage: 5GiWhen to use it
- A database team runs a three-node MySQL cluster as a StatefulSet so each pod gets a stable hostname (mysql-0, mysql-1, mysql-2) for replica configuration.
- A Kafka operator uses a StatefulSet to ensure each broker's persistent volume is reattached to the same pod after a restart, preserving broker data.
- An ops team scales down a StatefulSet one pod at a time (in reverse ordinal order) to safely drain Cassandra ring nodes before decommissioning.
More examples
StatefulSet for a database cluster
Defines a 3-replica MySQL StatefulSet with per-pod PersistentVolumeClaims so each instance retains its own data volume.
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: mysql
spec:
serviceName: mysql-headless
replicas: 3
selector:
matchLabels:
app: mysql
template:
metadata:
labels:
app: mysql
spec:
containers:
- name: mysql
image: mysql:8.0
env:
- name: MYSQL_ROOT_PASSWORD
valueFrom:
secretKeyRef:
name: mysql-secret
key: password
volumeClaimTemplates:
- metadata:
name: data
spec:
accessModes: [ReadWriteOnce]
resources:
requests:
storage: 10GiStable DNS names per pod
Shows the stable, predictable pod DNS names provided by the headless Service, enabling replicas to discover each other.
kubectl get pods -l app=mysql
# mysql-0, mysql-1, mysql-2
# Each pod is reachable at:
# mysql-0.mysql-headless.default.svc.cluster.local
# mysql-1.mysql-headless.default.svc.cluster.localOrdered scale-down
Demonstrates that StatefulSets scale down in reverse ordinal order, protecting data integrity in clustered databases.
kubectl scale statefulset mysql --replicas=2
# Kubernetes deletes mysql-2 first, then mysql-1 if reducing further
# Ensures orderly Cassandra/Kafka decommissioning
Discussion