Deployments are the default way to run applications on Kubernetes, and for most stateless services they are exactly right. But databases, message queues and other clustered systems need things a Deployment cannot give them: a stable name, a predictable start order and storage that stays with the same replica. That is the job of the Kubernetes StatefulSet.
This guide compares StatefulSet vs Deployment in plain terms, explains how a StatefulSet works under the hood, walks through a YAML example and shows when to choose each workload type.
What Is a Deployment?
A Deployment manages a set of identical, interchangeable pods through a ReplicaSet. Pods get random names such as web-7d9f8c6b5-x2kqp, can be scheduled anywhere and are replaced freely during rolling updates. If a pod dies, a new one with a new name and a new IP takes its place, and nobody cares which one serves a request. This model is perfect for stateless web servers, APIs and workers that keep their state in an external database or cache.
What Is a StatefulSet?
A StatefulSet is a Kubernetes workload controller for applications that need a persistent identity. Each pod in a StatefulSet gets a fixed ordinal name, such as db-0, db-1 and db-2, that survives rescheduling. Each pod also gets its own PersistentVolumeClaim, and a StatefulSet always reattaches the same volume to the same pod name, even if the pod moves to another node.
In short, a Deployment treats pods like cattle, while a StatefulSet treats them like named members of a cluster that remember who they are.
Key Features of a StatefulSet
Stable Network Identity
A StatefulSet is paired with a headless Service (a Service with clusterIP set to None). This gives every pod a stable DNS name in the form pod-name.service-name.namespace.svc.cluster.local, for example db-0.db.default.svc.cluster.local. Clustered software such as PostgreSQL replication, Kafka, ZooKeeper or Elasticsearch uses these names to find peers and elect leaders.
Stable Persistent Storage
The volumeClaimTemplates section of a StatefulSet creates one PersistentVolumeClaim per pod, such as data-db-0 and data-db-1. When db-1 is rescheduled, it reconnects to data-db-1. By default, scaling down or deleting a StatefulSet does not delete these claims, which protects your data. Newer Kubernetes versions let you change this with the persistentVolumeClaimRetentionPolicy field.
Ordered Deployment and Scaling
With the default OrderedReady pod management policy, a StatefulSet creates pods one at a time in order, waiting for each pod to be running and ready before starting the next. Scaling down removes pods in reverse order, starting with the highest ordinal. If your application does not need strict ordering, set podManagementPolicy to Parallel for faster scaling.
Ordered Rolling Updates
The RollingUpdate strategy of a StatefulSet updates pods one by one from the highest ordinal to the lowest. The partition setting lets you do a staged or canary rollout: with partition set to 2, only pods with an ordinal of 2 or higher receive the new version.
StatefulSet vs Deployment: Side-by-Side
- Pod names: Deployment pods get random hash suffixes; StatefulSet pods get stable ordinals (app-0, app-1).
- Network identity: Deployment pods are reached through a load-balanced Service; each StatefulSet pod also has its own stable DNS name through a headless Service.
- Storage: Deployment pods usually share one volume or use none; a StatefulSet gives each pod a dedicated PersistentVolumeClaim.
- Start and stop order: Deployment pods start and stop in parallel; a StatefulSet starts them in order and removes them in reverse.
- Rolling updates: Deployments use maxSurge and maxUnavailable; a StatefulSet updates one pod at a time and supports partitions.
- Typical workloads: Deployments run web apps, APIs and stateless workers; a StatefulSet runs databases, queues and distributed coordination systems.
StatefulSet YAML Example
Below is a simplified StatefulSet for a three-node PostgreSQL setup, with the headless Service it depends on:
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
ports:
- containerPort: 5432
volumeMounts:
- name: data
mountPath: /var/lib/postgresql/data
volumeClaimTemplates:
- metadata:
name: data
spec:
accessModes: ["ReadWriteOnce"]
resources:
requests:
storage: 20Gi
Applying this creates pods db-0, db-1 and db-2 in order, each with its own 20Gi volume. Note that this example only handles identity and storage; real replication, failover and backups need extra configuration inside PostgreSQL or a Kubernetes Operator.
When to Use a StatefulSet
- Databases such as PostgreSQL, MySQL, MongoDB and Cassandra.
- Message brokers and streaming platforms such as Kafka and RabbitMQ.
- Coordination services such as ZooKeeper and etcd.
- Search clusters such as Elasticsearch and OpenSearch.
- Any application where replicas have distinct roles, such as a primary and replicas, or need to address each other by name.
When a Deployment Is the Better Choice
- The application is stateless and stores data in an external database, cache or object storage.
- Any replica can handle any request.
- You want fast parallel scaling and simple autoscaling with the Horizontal Pod Autoscaler.
- Pods can safely share one read-only volume or need no storage at all.
A common anti-pattern is choosing a StatefulSet just because an application writes a few temporary files. If the data does not need to survive pod replacement, a Deployment with an emptyDir volume is simpler.
StatefulSet Limitations and Gotchas
- Kubernetes does not manage your data. A StatefulSet guarantees identity and storage, not replication, backup or failover. Those remain the application's job, or an Operator's. Read What Are Kubernetes Operators? to see how CloudNativePG, Strimzi and similar tools automate this.
- Volumes are tied to zones. Block storage is usually zonal, so a pod may only reschedule to nodes in the zone where its volume lives.
- Stuck rollouts. With OrderedReady, a broken pod blocks the rollout; you may need to fix the template and delete the failing pod manually.
- Deleting is not cleanup. Leftover PersistentVolumeClaims keep costing money after a StatefulSet is removed, so clean them up deliberately.
- Health checks matter more. Because pods start in order, slow or wrong readiness checks delay the whole StatefulSet. Our guide to Kubernetes probes explains how to tune them.
StatefulSet Best Practices
- Always create the headless Service before the StatefulSet and set serviceName correctly.
- Choose a StorageClass with volume expansion and the right performance tier.
- Add a PodDisruptionBudget so voluntary disruptions such as node drains never remove too many replicas at once.
- Use pod anti-affinity or topology spread constraints to keep replicas on different nodes and zones.
- Set accurate resource requests and limits so stateful pods are not evicted under pressure.
- Back up volumes with tools such as Velero or your database's native backup, and test restores regularly.
- Consider a managed cloud database when you do not need to run the database inside Kubernetes.
Conclusion
The StatefulSet vs Deployment choice comes down to identity. If every replica is interchangeable, use a Deployment for its simplicity and speed. If replicas need stable names, their own persistent storage and a predictable order, use a StatefulSet, and pair it with good health checks, backups and, for complex databases, an Operator. To keep building your Kubernetes skills, explore our Kubernetes tutorial for beginners and Helm charts guide.


