Kubernetes StatefulSets vs Deployments: When to Use Each

Deployments are the go-to workload type for stateless applications. StatefulSets are designed for stateful workloads that require stable identities, ordered operations, and persistent storage. Choosin

Introduction#

Deployments are the go-to workload type for stateless applications. StatefulSets are designed for stateful workloads that require stable identities, ordered operations, and persistent storage. Choosing the wrong type causes operational problems.

Key Differences#

Property Deployment StatefulSet
Pod names Random: app-7d4b9c-xkz9p Ordered: app-0, app-1, app-2
Pod identity Interchangeable Stable, persistent
Scaling order Parallel (all at once) Sequential (0, 1, 2…)
Rolling update order Random Reverse order (2, 1, 0)
Persistent volumes Shared or none Each pod gets its own PVC
DNS Single service DNS Individual DNS per pod
Deletion Parallel Reverse order

StatefulSet Example: Redis Cluster#

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
apiVersion: apps/v1
kind: StatefulSet
metadata:
  name: redis
  namespace: production
spec:
  serviceName: redis-headless   # required: headless service for stable DNS
  replicas: 3
  selector:
    matchLabels:
      app: redis
  template:
    metadata:
      labels:
        app: redis
    spec:
      containers:
      - name: redis
        image: redis:7-alpine
        ports:
        - containerPort: 6379
        volumeMounts:
        - name: data
          mountPath: /data
        command:
        - redis-server
        - --appendonly yes
        - --cluster-enabled yes
        - --cluster-config-file /data/nodes.conf
  volumeClaimTemplates:          # each pod gets its own PVC
  - metadata:
      name: data
    spec:
      accessModes: [ReadWriteOnce]
      storageClassName: fast-ssd
      resources:
        requests:
          storage: 10Gi
---
# Headless service: provides DNS for individual pods
apiVersion: v1
kind: Service
metadata:
  name: redis-headless
  namespace: production
spec:
  clusterIP: None      # headless: no virtual IP
  selector:
    app: redis
  ports:
  - port: 6379

With this setup, each pod gets stable DNS:

  • redis-0.redis-headless.production.svc.cluster.local
  • redis-1.redis-headless.production.svc.cluster.local
  • redis-2.redis-headless.production.svc.cluster.local

Stable Identity Matters for Consensus#

Distributed systems using consensus algorithms (Raft, Paxos) require stable identities. Pods must be able to refer to specific peers.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# Example: etcd StatefulSet init container bootstrapping
initContainers:
- name: init-etcd
  image: busybox
  command:
  - sh
  - -c
  - |
    # Determine this pod's index from hostname (e.g., etcd-2 → index 2)
    IDX=${HOSTNAME##*-}
    PEERS=""
    for i in $(seq 0 2); do
      PEERS="${PEERS}etcd-${i}=http://etcd-${i}.etcd-headless:2380,"
    done
    echo "--initial-cluster ${PEERS%,}" > /etc/etcd/cluster-config

Ordered Rolling Updates#

StatefulSets update pods in reverse order and wait for each pod to become Ready before proceeding.

1
2
3
4
5
spec:
  updateStrategy:
    type: RollingUpdate
    rollingUpdate:
      partition: 2    # only update pods with index >= 2 (canary)
1
2
3
4
5
6
# Update only pod-2 first (partition=2 means pods 0,1 keep old version)
kubectl patch statefulset redis -p '{"spec":{"updateStrategy":{"rollingUpdate":{"partition":2}}}}'
kubectl set image statefulset/redis redis=redis:7.2-alpine

# Verify pod-2 updated, then roll out to the rest
kubectl patch statefulset redis -p '{"spec":{"updateStrategy":{"rollingUpdate":{"partition":0}}}}'

PVC Lifecycle#

StatefulSet PVCs are not deleted when the StatefulSet is deleted. This protects data but requires manual cleanup.

1
2
3
4
5
6
7
8
# Delete StatefulSet without deleting pods (for maintenance)
kubectl delete statefulset redis --cascade=orphan

# Manually delete PVCs after confirming data is backed up
kubectl delete pvc -l app=redis -n production

# Or use the newer cascade policy
kubectl delete statefulset redis --cascade=foreground

When to Use Deployments vs StatefulSets#

Use StatefulSet for:

  • Databases (PostgreSQL, MySQL, MongoDB, Cassandra)
  • Distributed caches (Redis Cluster)
  • Message brokers (Kafka, Pulsar, RabbitMQ in cluster mode)
  • Consensus systems (etcd, ZooKeeper)
  • Any workload where individual pod identity matters to the application

Use Deployment for:

  • Stateless HTTP APIs
  • Background workers consuming from queues
  • Any workload where pods are interchangeable

A common mistake is running a database as a Deployment. This works until pods restart and get new identities — breaking cluster membership and data locality.

Conclusion#

StatefulSets give you stable pod names, ordered operations, and per-pod persistent storage. The trade-off is more complex lifecycle management. Deployments are simpler and sufficient for stateless workloads. The rule of thumb: if your application cares which pod it is or needs persistent local storage, use a StatefulSet.

Contents