Blog · September 30, 2025 · Updated on September 7, 2026 · 9 min read

Kubernetes StatefulSet vs Deployment

Row of steel-framework server racks with networking devices and cables in a modern data center
Photo: Brett Sayles / Pexels

A Kubernetes StatefulSet is the workload resource for containers that need to keep state beyond their own restart, such as databases or message brokers. Unlike a Deployment, it gives every Pod a fixed, recurring identity and creates, updates and deletes the Pods in a strict order. A Deployment, by contrast, treats its Pods as interchangeable and does not care which one comes first.

I run production-grade Kubernetes clusters with k3s on Hetzner Cloud and covered the StatefulSet in chapter 6 of my Kubernetes book published by Rheinwerk. Where the line between Deployment and StatefulSet actually runs is something I see drawn wrong in projects on a regular basis, usually because an application is treated as stateless when it is not. You can find all my Kubernetes articles collected on the Kubernetes page.

Kubernetes StatefulSet vs. Deployment: the difference that matters

A Deployment fits applications where every Pod is interchangeable: a web server, an API, a worker. If one fails, a new one replaces it, with no regard for order or name. A StatefulSet is built for the opposite case: applications where every Pod carries its own identity and often its own data, such as the nodes of a database cluster or a message broker.

The difference shows up first in the name. A Pod from a Deployment gets a random suffix like web-6d8f9b7c9d-x2kpl, a new one after every restart. A Pod from a StatefulSet carries a sequential index starting at 0 and keeps it across restarts and even recreation: web-0, web-1, web-2.

Deployment StatefulSet
Pod name random suffix, changes on every restart sequential index, stays stable
Start and delete order all Pods at once, no fixed order one Pod after another, only once the previous one is ready
Network name changes with every new Pod stays stable for the Pod's lifetime
Volume per Pod shared across replicas or none at all its own volume per Pod via volumeClaimTemplates
Typical use stateless web and API services databases, message brokers, distributed systems with identity

For more on how a Deployment rolls out and replaces Pods, see Kubernetes Deployment: Rollouts Explained.

Stable identity: how StatefulSets name and order Pods

A stable name alone would not be enough if the Pods could not find each other on the network. That is why a StatefulSet requires a headless Service, recognizable by clusterIP: None and referenced through serviceName in the StatefulSet manifest. Through that Service, every Pod gets its own predictable DNS name following the pattern <pod-name>.<service-name>.<namespace>.svc.cluster.local. A broker node can then address another node directly by name, with no load balancer in between.

That predictability is the real reason distributed systems like RabbitMQ, Kafka or Elasticsearch need StatefulSets: at startup, the nodes have to find each other and form a cluster, and that only works if their names and addresses are stable. A Deployment could not deliver this, because every restart produces a new, random name.

If you run your cluster in a cloud like Hetzner or AWS, there is a second constraint: block storage there is often bound to a specific zone. Kubernetes then has to make sure, on every restart, to place the Pod back in a zone where its volume actually lives. The scheduler handles this automatically as long as the volume mapping is set up correctly, but it explains why a StatefulSet Pod sometimes waits longer for its spot than a Deployment Pod.

A Kubernetes StatefulSet example with volumeClaimTemplates

The difference gets most concrete in a manifest. The following example rolls out three RabbitMQ nodes. Every Pod gets its own volume through volumeClaimTemplates, without you having to create a PersistentVolumeClaim yourself.

apiVersion: v1
kind: Service
metadata:
  name: rabbitmq
spec:
  clusterIP: None
  selector:
    app: rabbitmq
  ports:
    - port: 5672
      name: amqp
---
apiVersion: apps/v1
kind: StatefulSet
metadata:
  name: rabbitmq
spec:
  serviceName: "rabbitmq"
  replicas: 3
  selector:
    matchLabels:
      app: rabbitmq
  template:
    metadata:
      labels:
        app: rabbitmq
    spec:
      containers:
        - name: rabbitmq
          image: rabbitmq:management
          ports:
            - containerPort: 5672
              name: amqp
          volumeMounts:
            - name: rabbitmq-data
              mountPath: /var/lib/rabbitmq
  volumeClaimTemplates:
    - metadata:
        name: rabbitmq-data
      spec:
        accessModes: ["ReadWriteOnce"]
        storageClassName: "standard"
        resources:
          requests:
            storage: 5Gi

The headless Service above provides the stable DNS names, the StatefulSet below provides order and identity. The important part is the volumeClaimTemplates section: it looks like a normal PersistentVolumeClaim manifest, but Kubernetes instantiates it once per Pod. Scale from three to five replicas, and two new volumes appear automatically, one for rabbitmq-3 and one for rabbitmq-4. How PV, PVC and StorageClass actually work together in detail is covered in Kubernetes Persistent Volumes and PVCs.

Roll the manifest out and kubectl get pods shows you exactly the order that defines a StatefulSet: rabbitmq-0 gets created and has to become ready before rabbitmq-1 starts, and only after that comes rabbitmq-2. Getting those three nodes to actually form a RabbitMQ cluster takes extra configuration in the application, peer discovery and a shared Erlang cookie among them. What the StatefulSet provides is the precondition: stable names the nodes can remember each other by.

Scaling and updates: Pod management policy and partitions

Two levers determine how carefully a StatefulSet handles its Pods. The first is podManagementPolicy. In its default value OrderedReady, the StatefulSet starts and stops Pods strictly one after another, exactly as in the example above. Set it to Parallel and the StatefulSet behaves like a Deployment when scaling, starting all Pods at once. This does not change anything about updates themselves, those always run sequentially.

The second lever is updateStrategy. The default RollingUpdate replaces Pods one at a time, starting with the highest index. The extra option partition lets you define from which index updates are even allowed. With five replicas and the partition set to 3, the StatefulSet only updates rabbitmq-3 and rabbitmq-4, while rabbitmq-0 through rabbitmq-2 stay untouched. That is a simple way to test an update on a few nodes first before you lower the partition step by step and roll out the rest.

What actually happens to the volume itself when you scale down or delete the StatefulSet entirely depends on the PVC retention policy. By default, every volume stays around even after its Pod disappears, so scaling back up gives you the old data back. The finer rules, including exactly when Kubernetes deletes a volume for good, are covered in Kubernetes Persistent Volumes and PVCs.

Database in the cluster: build your own StatefulSet or use an operator

The question I hear most often is not "how do I write a StatefulSet" but "should I even run my database inside Kubernetes at all". A handwritten StatefulSet like the RabbitMQ example above is fine for a single broker or a single instance. Once replication, automatic failover or consistent backups enter the picture, a StatefulSet on its own quickly becomes too little.

That is exactly what operators are for: they manage a database as its own Kubernetes resource. For PostgreSQL, for example, CloudNativePG has become a common choice: you only describe how many instances you want and which storage class to use, and the operator handles Pods, volumes, failover and consistent backups. It deliberately manages those instances through its own controller instead of putting a StatefulSet in between. The project documentation gives concrete reasons: a StatefulSet cannot resize PVCs, and it does not know how several volumes of one instance relate to each other. If someone deletes the PVC holding the WAL files, a StatefulSet would simply recreate it and leave a corrupted PostgreSQL instance behind. The custom controller instead keeps the cluster state in the API server and handles failover and switchover itself (CloudNativePG documentation). In my own clusters, I now reach for an operator for databases almost every time and only write a plain StatefulSet when no suitable operator exists or the requirements are genuinely simple.

How you back up a StatefulSet's volumes independently of the operator is covered in Kubernetes Backup: etcd and Volumes.

Frequently asked questions

When do I need a StatefulSet instead of a Deployment?

Whenever your application has to keep state beyond a Pod's restart and the individual instances differ from each other, for example because they hold their own data or a fixed role within the cluster. Databases, message brokers and other distributed systems with node identity are the classic cases. For stateless applications, the Deployment remains the right choice.

What happens to the volume when a Pod in a StatefulSet dies?

As long as only the Pod fails and the StatefulSet is neither scaled down nor deleted, its volume stays put. Kubernetes creates a new Pod with the same index and the same volume mapping, so the data survives. Only a deliberate scale-down or deletion triggers the retention rules, which I cover in detail in the article on Persistent Volumes.

What is the difference between a PV and a PVC?

In short: a PersistentVolume (PV) is the actual storage, a PersistentVolumeClaim (PVC) is a Pod's request for that storage. I explain this with examples in Kubernetes Persistent Volumes and PVCs.

Should I run a database directly in Kubernetes?

That depends on your team and your requirements. A StatefulSet on its own does not automatically cover replication, failover and backups, a suitable operator comes closer. If you do not want to carry that effort yourself, a managed database outside the cluster is often the better fit.

Can I change the Pod management policy afterward?

No. podManagementPolicy can only be set when the StatefulSet is created, not changed afterward. To switch it, you have to delete the StatefulSet and recreate it, which needs careful planning for production applications.

Where to go next

Once you know your application needs a StatefulSet, the next question is almost always about the storage behind it: which storage class, which access mode, which behavior on deletion. I answer that in Kubernetes Persistent Volumes and PVCs. If instead you want to check whether an application needs a StatefulSet at all, or can get by with a plain Deployment, Kubernetes Deployment: Rollouts Explained will help.

My suggestion for today: roll out the RabbitMQ manifest above in a test cluster and watch with kubectl get pods -w in which order the three nodes come up. Then delete a Pod in the middle as a test and see how the StatefulSet reacts.

In full detail with all examples, from the Pod management policy to the PVC retention policy, this is covered in chapter 6 of my book "Kubernetes: Practical Guide for Developers and DevOps Teams" (Rheinwerk Computing). You can find all the information about the book on my Kubernetes page.

Kevin Welter

Kevin Welter

Developer, IT architect, author of technical books (Kubernetes, cloud infrastructures) and speaker. Runs his business with an AI workforce of eight AI employees and shows solo business owners in his community how to hire their first AI employee.

More about AI employees

Kubernetes from the basics to a production-ready cluster

Get the book