A Deployment's Pods are interchangeable: random names, any order, shared nothing. A database replica is not interchangeable. It has a name other members know, and a disk only it may write.
A StatefulSet runs Pods that keep their identity.
What is stable
StatefulSet db, replicas 3, serviceName db, volumeClaimTemplate data
db-0 ◀── data-db-0 ◀── pvc-…a1 db-0.db.<ns>.svc.cluster.local
db-1 ◀── data-db-1 ◀── pvc-…b2 db-1.db.<ns>.svc.cluster.local
db-2 ◀── data-db-2 ◀── pvc-…c3 db-2.db.<ns>.svc.cluster.local- Name:
<statefulset>-<ordinal>. A replaceddb-1is againdb-1. - DNS:
<pod>.<service>.<namespace>.svc.cluster.local, through a headless Service (clusterIP: None) named inspec.serviceName. - Storage: one claim per Pod from
volumeClaimTemplates, named<template>-<statefulset>-<ordinal>. The Pod mounts its own claim and no other.
The manifest
apiVersion: v1
kind: Service
metadata:
name: db
spec:
clusterIP: None
selector:
app: db
ports:
- port: 5432
---
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: 1GiThe template's volumeMounts names the claim template. No volumes section is needed; the controller adds one per Pod.
Order
create db-0 → wait until Running and Ready → db-1 → wait → db-2
scale down 3→1 delete db-2 → then db-1. Claims data-db-1, data-db-2 stay.
scale up 1→3 db-1 re-attaches data-db-1, db-2 re-attaches data-db-2.
update db-2 first, then db-1, then db-0 (highest ordinal down)kubectl rollout status sts db reports the update. kubectl rollout undo is not available for StatefulSets in this sandbox.
Partition
spec.updateStrategy.rollingUpdate.partition holds back an update: only Pods with ordinal >= partition get the new template. With 3 replicas and partition: 2, only db-2 updates. A partition equal to the replica count updates nothing.
kubectl get sts db -o jsonpath='{.spec.updateStrategy}'Deleting
kubectl delete sts db deletes the Pods and keeps the claims. Apply the same StatefulSet again and each Pod re-attaches its claim. Delete with --cascade=orphan and the Pods stay too.