What is a Kubernetes StatefulSet?
How does it differ from a Kubernetes Delpoyment?
A StatefulSet is a Kubernetes object designed for stateful applications. Unlike a Deployments, which treats all pods as interchangeable, a StatefulSet gives each pod a stable identity and persistent storage. This ends up being essential for applications that need to remember who they are and, perhaps most importantly, where their data lives (e.g. databases, message queues, distributed caches, and so on).
A deployment creates pods with random names (e.g. nginx-deployment-7fb94c8f4-x2jkl) and assigns them ephemeral storage. If a pod is replaced, it gets a new name and a fresh disk. For stateless applications, this is perfectly fine. For a database, however, losing its name and data on every reschedule would be a problem.
StatefulSet solves this by guaranteeing us with:
- Stable, unique pod names: pods get predictable names like
db-0,db-1,db-2instead of random suffixes; - Stable network identities: each pod gets a consistent DNS entry that follows the pattern
<pod-name>.<service-name>.<namespace>.svc.cluster.local; - Persistent storage: each pod gets its own PersistentVolumeClaim, which survives pod reschedules and restarts.
apiVersion: apps/v1kind: StatefulSetmetadata: name: dbspec: serviceName: 'db-service' replicas: 3 selector: matchLabels: app: db template: metadata: labels: app: db spec: containers: - name: db image: postgres:16 ports: - containerPort: 5432 volumeMounts: - name: data mountPath: /var/lib/postgresql/data volumeClaimTemplates: - metadata: name: data spec: accessModes: ['ReadWriteOnce'] resources: requests: storage: 10GiLet’s have a closer look:
- serviceName links the StatefulSet to a headless Services, which is required for stable DNS entries (more on this below);
- replicas works the same way as in a deployment (3 pods in this case);
- template is the pod template, identical to what we’d see in a deployment;
- volumeClaimTemplates is what makes StatefulSets special: it creates a separate PersistentVolumeClaim for each pod, so
db-0,db-1, anddb-2each get their own 10Gi volume.
For the stable DNS entries to work, a StatefulSet needs a headless service (a service with clusterIP: None). A headless service does not perform load balancing; instead, it returns the IP addresses of individual pods directly.
apiVersion: v1kind: Servicemetadata: name: db-servicespec: clusterIP: None selector: app: db ports: - port: 5432With this in place, db-0.db-service.default.svc.cluster.local always resolves to the pod named db-0, regardless of which node it is running on.
When we scale a StatefulSet up, Kubernetes creates pods in order (db-3, then db-4, and so on). When we scale down, it removes pods in reverse order (db-4, then db-3). This ordered approach is important for distributed systems that rely on peer discovery (each node knows its neighbours by name).
| Deployments | StatefulSets | |
|---|---|---|
| Pod names | Random | Stable and ordered |
| Storage | Ephemeral (unless explicitly configured) | Persistent, per pod |
| Scaling | Order does not matter | Pods created and removed in order |
| Best for | Stateless apps | Databases, queues, anything with state |