Skip to content
C Codeloom
DevOps

Kubernetes Deployments vs StatefulSets: When to Use Each

Understand the real differences between Deployments and StatefulSets in Kubernetes. Learn which workloads belong in each, with concrete YAML and rollout behavior.

·4 min read · By Codeloom
Beginner 11 min read

What you'll learn

  • What a Deployment guarantees and what it does not
  • Why StatefulSets exist and what stable identity means
  • How pod naming and DNS differ between the two
  • How rolling updates behave in each controller
  • A simple rule for picking the right one

Prerequisites

  • Comfortable with the Linux command line

A common moment of confusion when learning Kubernetes is hitting two controllers that both run pods on your behalf and wondering which one to pick. Deployments are the workhorse for stateless services like web apps and APIs. StatefulSets exist for workloads that need a stable identity, ordered startup, or persistent per-pod storage — think databases, message brokers, and clustered caches. The distinction is not academic; the wrong choice will bite you during the first rolling update.

Deployments in one breath

A Deployment manages a ReplicaSet, which in turn manages Pods. The Pods are interchangeable. Each one gets a randomly suffixed name like web-7d9c4f8b6d-xk2vp. If a node dies, the scheduler launches a replacement somewhere else and nobody cares which pod handles the next request.

apiVersion: apps/v1
kind: Deployment
metadata:
  name: web
spec:
  replicas: 3
  selector:
    matchLabels:
      app: web
  template:
    metadata:
      labels:
        app: web
    spec:
      containers:
        - name: web
          image: nginx:1.27
          ports:
            - containerPort: 80

Updates roll out by spinning up new pods and tearing down old ones, in parallel batches controlled by maxSurge and maxUnavailable. It is fast, simple, and exactly what you want for a stateless workload.

What Deployments do not give you

Three things, mainly. First, no stable network identity — when a pod is replaced, its name and IP change. Second, no ordered startup — pods come up in whatever order the scheduler finds room for. Third, no per-pod persistent storage — if you attach a PersistentVolumeClaim to a Deployment, every replica shares it, which is rarely what a database wants.

If those properties matter, you need a StatefulSet.

StatefulSets in one breath

A StatefulSet also manages Pods, but with three additional guarantees: stable network identity, ordered deployment and termination, and per-pod persistent storage via volumeClaimTemplates.

apiVersion: apps/v1
kind: StatefulSet
metadata:
  name: db
spec:
  serviceName: db-headless
  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: 10Gi

The pods are named db-0, db-1, db-2 — not random. Each one gets its own PVC named data-db-0, data-db-1, data-db-2. They start in order (0 before 1 before 2) and terminate in reverse. Combined with a headless Service, each pod gets a DNS name like db-0.db-headless.default.svc.cluster.local, which is how cluster members find each other.

Rolling updates differ

A Deployment rolls all pods in parallel up to the maxSurge and maxUnavailable settings. Fast, but unpredictable order.

A StatefulSet rolls pods one at a time, starting from the highest ordinal and working down. Pod db-2 is updated and must reach Ready before db-1 begins. This matters because clustered databases often need a controlled rollout to maintain quorum.

Headless Services and DNS

StatefulSets are almost always paired with a headless Service, declared by setting clusterIP: None. Instead of one virtual IP that load-balances across pods, the Service publishes an A record per pod.

apiVersion: v1
kind: Service
metadata:
  name: db-headless
spec:
  clusterIP: None
  selector:
    app: db
  ports:
    - port: 5432

Now a client can connect to a specific replica by name, which is exactly what replication setups expect.

Scaling

Scaling a Deployment up or down is essentially free — pods are fungible. Scaling a StatefulSet is more delicate. Scaling up adds the next ordinal and provisions a new PVC. Scaling down terminates the highest ordinal but does not delete the PVC, on purpose — you might want the data back. Cleaning up storage after scale-down is a manual step you have to remember.

The simple decision rule

Ask one question: does this workload care which pod it is? If the answer is no, you want a Deployment. If the answer is yes — because it has a name in a cluster config, because it owns its own slice of data, because order matters — you want a StatefulSet.

kubectl get deploy
kubectl get sts

Run those side by side on a healthy cluster and you will usually see Deployments running web tier and API tier workloads, and StatefulSets running the data tier. That pattern is the default for a reason.

Pick the right controller up front and you avoid a painful migration later. Pick wrong and you will find out the first time a node reboots.