StatefulSets and Deployments are two Kubernetes API objects used to manage sets of identical Pods. They both make it easy to orchestrate multiple Pod replicas, but they have different features that specialize them for separate use cases.
Deployments offer declarative configuration to automate Pod updates and scaling operations, whereas StatefulSets include additional features that help you orchestrate stateful workloads in your cluster.
In this article, we’ll compare both types of objects and discuss when each should be used.
You will learn:
Stateful vs. stateless applications
The difference between StatefulSets and Deployments reflects the divide between stateful and stateless systems. As their name suggests, StatefulSets are designed to run your app’s stateful components, while Deployments are used for stateless ones.
A system is stateless when it doesn’t need to store any data within itself. Website and web app frontends are usually stateless, for example. On the other hand, applications such as databases are said to be stateful. They require persistent storage that outlives the lifecycle of individual container replicas.
Kubernetes is best known for managing stateless services. When an app is stateless, its Pods are fully interchangeable; scaling operations won’t result in the loss of any data. This is not true of stateful applications, however. To run a stateful service in Kubernetes, you’ll need to use a StatefulSet to ensure stable Pod replication and data persistence.
What is a Kubernetes StatefulSet?
Kubernetes StatefulSet is an API-object that’s purpose-built to support stateful application components. It creates a set of identically configured Pods from a spec you supply, but each Pod is assigned a non-interchangeable identity. Pods retain their identity if they have to be rescheduled or you scale the StatefulSet.
StatefulSets solve the challenges of running stateful services in Kubernetes. The persistent Pod identities permit storage volumes to be associated with specific Pods inside the StatefulSet. They also facilitate graceful scaling operations and rolling updates, where Pods are added and removed in a predictable order.
When to Use StatefulSets?
Let’s consider a simple example of running three replicas of a MySQL database server in Kubernetes. The deployment should be configured with one Pod in the primary role, handling read-write operations, and the remaining three Pods as MySQL read-only replicas.
Applications connecting to the database will always need to connect to the Pod that’s in the primary role in order to receive read-write access. This wouldn’t be possible if a Deployment or ReplicaSet was used, as scheduling or replication changes would generate new Pod identities. The application would have no way of knowing which Pod is the primary MySQL instance.
StatefulSets eliminate this problem. Each Pod in the StatefulSet is assigned a predictable and consistent network identity in the form <statefulset-name>-<pod-ordinal-index>. The four Pods in the MySQL deployment would be named as follows:
mysql-0– First Pod, in the primary rolemysql-1– Read-only replicamysql-2– Read-only replica
Now other applications can connect to mysql-0 to reliably interact with the MySQL primary instance. Because StatefulSets also guarantee ordered updates, the vital mysql-0 replica is only terminated if you scale down to zero. The read-only replicas will always be removed first.
Furthermore, the persistent storage characteristics of StatefulSets mean that each Pod will always have its own storage volume reattached, even after it’s rescheduled. Each replica, therefore, maintains its own copy of the database, ensuring reliable, independent data replication.
Similarly, Pods support stable network identities using a headless service that you must separately create.
Example: Creating a Kubernetes StatefulSet
The following manifests create a basic stateful application that runs three replicas of an NGINX container. Each Pod gets its own persistent volume and a stable network identity via a headless Service.
apiVersion: v1
kind: Service
metadata:
name: app
labels:
app: app
spec:
clusterIP: None # Headless service for stable DNS records
selector:
app: app
---
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: app
spec:
serviceName: app # Must match the headless Service name
selector:
matchLabels:
app: app
replicas: 3
template:
metadata:
labels:
app: app
spec:
containers:
- name: app
image: nginx:latest
volumeMounts:
- name: data
mountPath: /usr/share/nginx/html
volumeClaimTemplates:
- metadata:
name: data
spec:
accessModes: ["ReadWriteOnce"]
resources:
requests:
storage: 5GiSave the manifest to statefulset.yaml, then use Kubectl to apply it to your cluster:
$ kubectl apply -f statefulset.yaml
service/app created
statefulset.apps/app createdThe requested three Pods will be created for the StatefulSet:
$ kubectl get pods
NAME READY STATUS RESTARTS AGE
app-0 1/1 Running 0 18s
app-1 1/1 Running 0 16s
app-2 1/1 Running 0 14sEach Pod has:
- A predictable name (
app-0,app-1,app-2). - Its own PersistentVolumeClaim derived from the
datatemplate (for example,data-app-0,data-app-1, anddata-app-2).
If you change your manifest’s spec.replicas field to 5 and reapply it:
$ kubectl apply -f statefulset.yaml
statefulset.apps/app configured
$ kubectl get pods
NAME READY STATUS RESTARTS AGE
app-0 1/1 Running 0 2m7s
app-1 1/1 Running 0 2m5s
app-2 1/1 Running 0 2m3s
app-3 1/1 Running 0 36s
app-4 1/1 Running 0 34sThe new Pods continue the ordinal sequence. Their associated PVCs are created automatically using the same naming pattern.
If you then reduce the replica count back to 3 and apply the manifest again:
$ kubectl apply -f statefulset.yaml
statefulset.apps/app configured
$ kubectl get pods
NAME READY STATUS RESTARTS AGE
app-0 1/1 Running 0 13m
app-1 1/1 Running 0 12m
app-2 1/1 Running 0 12mKubernetes terminates Pods in reverse order of creation (app-4 and app-3 first), ensuring the original three Pods — and their associated volumes — remain intact.
What is a Kubernetes Deployment?
Deployment is an API object used to manage Pods and ReplicaSets that are part of stateless applications. They support declarative configuration, rollouts, and rollbacks. You use them to automate Pod updates.
Because Deployments use a declarative management model, you only need to define what your desired state looks like. When you apply a Deployment manifest, Kubernetes will automatically compare the state it describes to the current version in your cluster. The Deployment controller will then reconcile the existing state to the new desired state, which results in Pods being added and removed as required.
In practical terms, it allows you to change the number of Pod replicas by adjusting the value in your Deployment’s manifest. Kubernetes will automatically add the correct number of new Pods, or remove existing ones, to achieve the rollout. Deployments also allow you to pause a rollout if you detect a problem and rollback to a previous state.
When to use Deployments?
Use a Deployment to run stateless applications that need to benefit from declarative updates and rollbacks. They permit you to rollout changes safely, without the threat of downtime.
Deployments automatically create Pods and ReplicaSets from a consistent specification you define. This removes much of the administrative burden of configuring, changing, and scaling containerized applications. The Deployment controller handles the orchestration task and will allocate each Pod to the most appropriate Node.
In addition, using a Deployment makes your application more resilient to any failures that occur in your cluster. The Deployment controller will ensure the specified number of Pods are kept running; they’ll be rescheduled automatically after Node failures.
Wrapping Pods in a Deployment is always preferable to running them “bare,” without management by a controller, because those Pods can’t be directly scaled or replicated across your cluster’s Nodes.
Example: Creating a Kubernetes Deployment
Consider the following simple manifest for a Kubernetes Deployment YAML file, which starts three replicas of a Pod running the NGINX web server:
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx
spec:
selector:
matchLabels:
app: nginx
replicas: 3
template:
metadata:
labels:
app: nginx
spec:
containers:
- name: nginx
image: nginx:latestCopy this manifest, save it to deployment.yaml in your working directory, and then use Kubectl to apply it to your cluster:
$ kubectl apply -f deployment.yaml
deployment.apps/nginx createdKubernetes will create three Pods to satisfy the declared replica count:
$ kubectl get pods
NAME READY STATUS RESTARTS AGE
nginx-654975c8cd-c5cqj 1/1 Running 0 13s
nginx-654975c8cd-lsw22 1/1 Running 0 13s
nginx-654975c8cd-tkf4s 1/1 Running 0 13sTry changing the spec.replicas field of your manifest to 5, then reapply it to your cluster and check the updated Pod list:
$ kubectl apply -f deployment.yaml
deployment.apps/nginx configured
$ kubectl get pods
NAME READY STATUS RESTARTS AGE
nginx-654975c8cd-7tgnq 1/1 Running 0 43s
nginx-654975c8cd-c5cqj 1/1 Running 0 106s
nginx-654975c8cd-lsw22 1/1 Running 0 106s
nginx-654975c8cd-sfj7m 1/1 Running 0 43s
nginx-654975c8cd-tkf4s 1/1 Running 0 106sYou can see that two new Pods have been created to match the revised replica count.
Now revert the replica count to 3 and apply the updated manifest once more:
$ kubectl apply -f deployment.yaml
deployment.apps/nginx configured
$ kubectl get pods
NAME READY STATUS RESTARTS AGE
nginx-654975c8cd-7tgnq 1/1 Running 0 3m4s
nginx-654975c8cd-c5cqj 1/1 Running 0 4m7s
nginx-654975c8cd-sfj7m 1/1 Running 0 3m4sThe output shows that Kubernetes has randomly terminated two Pods to leave three remaining. This demonstrates how Pods in Deployments are considered to be interchangeable, so their identities and order of creation are not tracked.
StatefulSet vs. Deployment
A Deployment manages stateless applications with interchangeable Pods, while a StatefulSet is used for stateful applications requiring persistent identity and storage per Pod.
The decision whether to use a StatefulSet or a Deployment should be based on the characteristics of the service you’re deploying in your cluster. StatefulSets are essential when your application manages data that must persist across Pod restarts or rescheduling. Deployments provide a simpler, more flexible solution for horizontal scaling of stateless services.
Here’s a quick rundown of their main features and differences:
| Feature | StatefulSet | Deployment |
| Stateful/Stateless applications | Stateful | Stateless |
| Pod identities | Pods are assigned a persistent identifier, derived from the StatefulSet’s name and their ordinal creation index. | Pods are assigned random identifiers, derived from the Deployment’s name and a unique random string. |
| Pod interchangeability | Pods in a StatefulSet are not interchangeable. It’s expected that each Pod has a specific role, such as always running as a primary or read-only replica for a database application. | All Pods are identical, so they’re interchangeable and can be replaced at any time. |
| Rollout ordering | Pods are guaranteed to be created and removed in sequence. When you scale down the StatefulSet, Kubernetes will terminate the most recently created Pod. | No ordering is supported. When you scale down the Deployment, Kubernetes will terminate a random Pod. |
| Storage access | Each Pod is automatically associated with its own PersistentVolume via volumeClaimTemplates, so storage is tied to Pod identity. | Deployments don’t automatically create per-Pod volumes; you usually mount shared volumes or manually managed PVCs. Pods are treated as interchangeable from a storage perspective. |
Kubernetes StatefulSet and Deployment use cases
StatefulSets and Deployments both manage Pods, but they’re targeted at specific use cases.
Choose a StatefulSet in the following scenarios:
- You’re running a stateful application that requires stable, persistent storage, such as a replicated deployment of a database, file server, key-value store, or messaging queue.
- Pods are non-interchangeable because they each have specific roles, such as being a primary, replica, or worker.
- You require predictable sequenced rollouts, with terminations that occur in reverse order.
Choose a Deployment when the following criteria apply:
- Your application is stateless; it does not require persistent storage, or all Pods can share the same storage volume.
- You require multiple replicas of a Pod to be created from one declaratively defined configuration.
- You need controlled rollouts and rollbacks, based on changes you make to your declared state.
Common mistakes with StatefulSets and Deployments
Although the concepts behind StatefulSets and Deployments are straightforward, it is easy to misuse them in real clusters.
- Running a database on a Deployment – A common mistake is running a database or message queue in a Deployment because it’s familiar. Deployments treat Pods as interchangeable and don’t give each Pod its own identity or storage. This can cause data loss or failed failover when Pods are rescheduled or scaled. StatefulSets are better for these workloads because each Pod gets a stable identity and dedicated volume.
- Using a StatefulSet for a stateless service – The reverse issue is using a StatefulSet for a stateless service like a frontend API or web app. These Pods don’t need stable identities or dedicated volumes, so a Deployment is usually better. Deployments offer simpler scaling and rollouts with fewer moving parts, reducing operational overhead.
- Assuming storage means backups – StatefulSets ensure the same volume is reattached to the same Pod identity, but they do not provide backups or disaster recovery. You still need storage that supports snapshots, replication, or external backups, plus a process to restore data when needed.
- Forgetting the headless Service – StatefulSets rely on a headless Service to give each Pod a stable DNS name. If you forget to create it, or use a normal ClusterIP Service instead, clients may not reliably reach the correct Pod. Each StatefulSet should reference its headless Service via the serviceName field in the spec.
What is the Difference Between a StatefulSet, a Deployment, and a DaemonSet?
As we’ve discussed in this article, StatefulSets are used to run stateful applications in Kubernetes, whereas Deployments support declarative updates for stateless Pods. DaemonSets are a third kind of Pod controller that you can also use.
A DaemonSet ensures that all or some of the Nodes in your cluster are always running a replica of a Pod. When new Nodes join the cluster, active DaemonSets will automatically start new Pods on those Nodes. This behavior means DaemonSets are ideal for monitoring systems, logging agents, and hardware integrations, where each Node in your cluster must permanently run an instance of the application.
Key points
In this article, we’ve explained the differences between the Kubernetes StatefulSet and Deployment objects. You’ve learned how you can use the objects to easily run both stateful and stateless applications inside your clusters.
A StatefulSet should be chosen for stateful components that require predictable Pod identities, ordered rollouts, and stable storage access. Select a Deployment instead when your service is stateless, doesn’t require persistent storage, and won’t be affected by changes to Pod identities.
If you need any assistance with managing your Kubernetes projects, take a look at Spacelift. It brings with it a GitOps flow, so your Kubernetes Deployments are synced with your Kubernetes Stacks, and pull requests show you a preview of what they’re planning to change. It also has an extensive selection of policies, which lets you automate compliance checks and build complex multi-stack workflows. You can check it for free by creating a trial account.
Manage Kubernetes Easier and Faster
Spacelift allows you to automate, audit, secure, and continuously deliver your infrastructure. It helps overcome common state management issues and adds several must-have features for infrastructure management.
