Deployments, ReplicaSets, and StatefulSets
Production Kubernetes applications rarely run as manually created pods. Pods are disposable runtime units: they can disappear during node failures, deployments, scaling events, evictions, and infrastructure maintenance. Something must continuously ensure that the required number and type of pods exist.
Deployments, ReplicaSets, and StatefulSets provide that workload-management layer. Deployments manage stateless applications and rolling releases, ReplicaSets maintain replica counts underneath Deployments, and StatefulSets manage workloads that require stable identities, ordered operations, or persistent storage relationships.
Choosing between them is primarily an architecture decision about identity, state, deployment behavior, scaling, and failure recovery. The wrong controller can create unnecessary operational complexity or, more seriously, violate assumptions made by stateful distributed systems.
Table of Contents
- Controllers and Desired State
- Deployments
- ReplicaSets
- StatefulSets
- Choosing the Right Controller
- Production Design Example
- Common Mistakes
- Production Checklist
- Conclusion
- More Articles to Read
Controllers and Desired State
Kubernetes controllers continuously compare desired state with actual state. Instead of permanently assigning an application to a specific server, a workload declares how many replicas should exist and how those replicas should run.
If actual state diverges because a pod crashes, a node disappears, or a deployment changes the pod template, controllers attempt to reconcile the cluster back toward the declared state.
Why Controllers Exist
Suppose an API requires four replicas:
Desired replicas: 4
Actual:
Pod A Running
Pod B Running
Pod C Running
Pod D Failed
Actual healthy replicas: 3
|
v
Controller detects
missing replica
|
v
Create Pod E
|
v
Healthy replicas: 4
The controller does not normally attempt to preserve Pod D as a permanent server. It restores the desired workload capacity by creating another pod.
This is why applications running on Kubernetes should generally tolerate pod replacement. More about pod and node failure boundaries can be found here: Kubernetes Explained: Pods, Nodes, and Clusters.
Workload Ownership Hierarchy
Deployments, ReplicaSets, and pods form a hierarchy rather than three competing ways to run the same application.
Deployment
|
| owns
v
ReplicaSet v3 ------------------+
| |
+---- Pod A |
+---- Pod B | current version
+---- Pod C |
|
ReplicaSet v2 | deployment history
|
+---- old pods removed -----+
A Deployment creates and manages ReplicaSets. ReplicaSets create and maintain pods. During a rollout, a Deployment can create a new ReplicaSet while gradually reducing replicas in the previous one.
A StatefulSet follows a different model. It directly manages pods with predictable identities and can associate each replica with persistent storage.
| Controller | Main Responsibility | Pod Identity | Typical Use |
|---|---|---|---|
| Deployment | Stateless replicas and application rollouts | Disposable | APIs, web applications, stateless workers |
| ReplicaSet | Maintain a specific number of matching pods | Disposable | Usually managed by Deployment |
| StatefulSet | Ordered replicas with stable identities | Stable | Databases, clustered stateful software |
Deployments
A Deployment is the standard controller for stateless long-running applications. It manages replicas, pod-template changes, rolling updates, and rollout history through ReplicaSets.
Typical workloads include REST APIs, frontend applications, internal services, and stateless consumers where one healthy replica can replace another without preserving the identity of the previous pod.
Rolling Deployments
Consider a shipment API running six replicas. A Deployment can declare the workload and control how version changes are rolled out:
apiVersion: apps/v1
kind: Deployment
metadata:
name: shipment-api
spec:
replicas: 6
strategy:
type: RollingUpdate
rollingUpdate:
maxUnavailable: 1
maxSurge: 2
selector:
matchLabels:
app: shipment-api
template:
metadata:
labels:
app: shipment-api
spec:
containers:
- name: api
image: registry.example.com/shipment-api:4.8.0
ports:
- containerPort: 8000
resources:
requests:
cpu: "500m"
memory: "512Mi"
limits:
memory: "1Gi"
readinessProbe:
httpGet:
path: /health/ready
port: 8000
periodSeconds: 5
maxUnavailable: 1 allows at most one desired replica to be unavailable during the rollout. maxSurge: 2 permits Kubernetes to temporarily create up to two additional pods above the desired replica count.
For six replicas, a rollout can therefore temporarily require capacity for as many as eight pods.
This matters in production because rolling deployments require temporary cluster headroom. If nodes have enough resources for exactly six replicas but no additional capacity, new pods may remain Pending and prevent the rollout from progressing as expected.
Readiness is equally important. A new pod should not receive production traffic merely because its process started. It should receive traffic only after initialization is complete and required application state is ready. More about these semantics can be found here: Health Checks, Readiness, and Liveness Probes.
Deployment Trade-Offs
Deployments make stateless application releases relatively simple, but their behavior depends on the application being compatible with overlapping versions.
Advantages:
- Automated maintenance of replica count.
- Rolling application updates.
- Controlled availability during deployments.
- Simple horizontal scaling.
- Rollback through previous ReplicaSets.
- Replaceable pods simplify infrastructure recovery.
Disadvantages:
- No stable identity for individual replicas.
- Rolling updates can temporarily increase resource consumption.
- Old and new application versions can coexist during deployment.
- Database or API incompatibility can break rolling releases.
- Bad readiness behavior can route traffic to unhealthy new versions.
When to use: Deployments are the default choice when replicas are interchangeable and persistent application state lives outside individual pods.
Rolling deployments also mean that version N and version N+1 can process traffic simultaneously. Database migrations, events, caches, and APIs must tolerate that overlap. Backward-compatible API evolution is discussed in greater depth here: API Versioning and Backward Compatibility.
ReplicaSets
A ReplicaSet has a narrower responsibility: ensure that a specified number of pods matching a selector exist. It does not provide the higher-level rollout behavior expected from Deployments.
ReplicaSets are critical to Kubernetes application operation, but application teams normally interact with them indirectly.
How ReplicaSets Work
A simplified ReplicaSet looks like this:
apiVersion: apps/v1
kind: ReplicaSet
metadata:
name: tracking-api
spec:
replicas: 3
selector:
matchLabels:
app: tracking-api
template:
metadata:
labels:
app: tracking-api
spec:
containers:
- name: api
image: registry.example.com/tracking-api:3.2.0
resources:
requests:
cpu: "250m"
memory: "256Mi"
If only two matching pods exist, the ReplicaSet creates another. If four matching pods exist, it can terminate one to restore the desired count of three.
The important mechanism is the label selector. The ReplicaSet does not fundamentally reason about application processes. It reasons about Kubernetes objects matching its selector.
This makes label design operationally important. Incorrect or overlapping selectors can cause controllers to manage unexpected resources.
Why ReplicaSets Are Rarely Managed Directly
A ReplicaSet can keep three replicas running, but it does not provide the same declarative application-update workflow as a Deployment.
When a Deployment's pod template changes, Kubernetes creates another ReplicaSet representing the new revision:
Before deployment
Deployment
|
+--- ReplicaSet A
|
+--- v1
+--- v1
+--- v1
During rollout
Deployment
|
+--- ReplicaSet A
| +--- v1
| +--- v1
|
+--- ReplicaSet B
+--- v2
+--- v2
After rollout
Deployment
|
+--- ReplicaSet A ---- 0 replicas
|
+--- ReplicaSet B
+--- v2
+--- v2
+--- v2
The previous ReplicaSet can remain with zero replicas as part of rollout history. This gives the Deployment a representation of previous revisions without requiring old application pods to keep running.
Use a Deployment instead of directly managing ReplicaSets for normal stateless applications. Direct ReplicaSet management is appropriate only for unusual cases where replica maintenance is required without Deployment rollout semantics.
StatefulSets
StatefulSets exist because some distributed applications cannot treat every replica as anonymous and interchangeable. A database replica, broker member, or consensus participant may require a stable network identity, persistent disk, or deterministic membership position.
A StatefulSet provides these guarantees while still allowing Kubernetes to manage scheduling and pod replacement.
Stable Identity and Storage
Suppose a distributed database has three members. With a Deployment, generated pod names might change whenever replicas are replaced. With a StatefulSet, replicas receive ordinal identities:
database-0
database-1
database-2
If database-1 disappears, its replacement keeps the database-1 identity rather than becoming an unrelated anonymous replica.
StatefulSets can also create a dedicated PersistentVolumeClaim for each replica:
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: database
spec:
serviceName: database
replicas: 3
selector:
matchLabels:
app: database
template:
metadata:
labels:
app: database
spec:
containers:
- name: database
image: registry.example.com/database:8.1.4
ports:
- containerPort: 5432
volumeMounts:
- name: data
mountPath: /var/lib/database
resources:
requests:
cpu: "2"
memory: "4Gi"
limits:
memory: "8Gi"
volumeClaimTemplates:
- metadata:
name: data
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 200Gi
The resulting relationship is conceptually:
database-0 ---- PVC data-database-0 ---- Persistent Volume A
database-1 ---- PVC data-database-1 ---- Persistent Volume B
database-2 ---- PVC data-database-2 ---- Persistent Volume C
If database-1 is recreated, Kubernetes can associate the replacement with its existing persistent claim rather than treating its storage as disposable.
This is fundamentally different from simply attaching one shared disk to multiple stateless replicas. Each member can maintain an explicit identity-to-storage relationship required by the distributed system.
Scaling and Failure Behavior
StatefulSets support ordered identity. When scaling from three replicas to five, new identities are normally created as database-3 and database-4. When scaling down, higher ordinals are removed first.
This predictability is valuable, but it does not automatically make a database safe to scale.
The application itself still controls concepts such as:
- leader election
- replication
- quorum
- data consistency
- partition ownership
- member initialization
- replica synchronization
- backup and recovery
Kubernetes can create database-3, but the database software must determine how database-3 joins the cluster and receives data.
Likewise, replacing a failed StatefulSet pod does not guarantee immediate recovery. A replacement might need to attach storage, replay transaction logs, synchronize data, or rejoin consensus before becoming useful.
StatefulSet provides infrastructure identity and lifecycle semantics; it does not implement application-level distributed consistency.
Choosing the Right Controller
The most useful decision boundary is whether replicas must retain individual identities. Stateless application instances usually should not. Stateful distributed systems sometimes must.
Using StatefulSets simply because an application writes data is not sufficient justification. Many applications write durable data to external databases while their application pods remain completely stateless.
Stateless Applications
Consider an order API where all authoritative state resides in PostgreSQL and Redis. Any API replica can process any request.
A Deployment fits this architecture because:
- replicas are interchangeable
- pod names have no business meaning
- local files are disposable
- traffic can move between replicas
- horizontal scaling means adding equivalent instances
If order-api-7f5c disappears and another pod replaces it, the application architecture should not care.
This model generally produces the simplest Kubernetes operations and should be preferred whenever application architecture allows it.
Stateful Distributed Systems
Now consider a three-member distributed storage system where every member owns persistent data and participates in replication.
A StatefulSet may be appropriate because:
- replicas need predictable identities
- each replica requires its own persistent volume
- startup or termination order may matter
- cluster membership may reference stable member names
StatefulSet should not be chosen merely because it sounds more durable. It introduces additional operational concerns around persistent volumes, topology, recovery, scaling, and data movement.
| Property | Deployment | ReplicaSet | StatefulSet |
|---|---|---|---|
| Primary purpose | Stateless workload management | Replica maintenance | Stateful workload management |
| Stable pod identity | No | No | Yes |
| Rolling updates | Yes | Not as a Deployment workflow | Yes, with stateful semantics |
| Persistent volume per replica | Not identity-oriented | Not identity-oriented | Native pattern |
| Ordered replicas | No | No | Yes |
| Typical direct use | Very common | Rare | Stateful systems |
| Scaling complexity | Low for stateless workloads | Low but limited functionality | Depends heavily on application |
| Operational complexity | Moderate | Low but rarely sufficient alone | Higher |
Production Design Example
Consider a logistics platform containing public APIs, asynchronous tracking processors, and a distributed event-storage system. The workloads have different identity and lifecycle requirements, so using one controller type for everything would be inappropriate.
The design separates stateless compute from stateful infrastructure and gives each workload the controller matching its failure and scaling model.
Architecture
Client Traffic
|
v
Shipment Service
|
+----------+----------+
| |
v v
Shipment API Deployment Message Broker
8 replicas |
|
+---------+---------+
| |
v v
Tracking Workers Sync Workers
Deployment Deployment
|
v
Event Storage Cluster
StatefulSet
+------+------+------+
| | |
store-0 store-1 store-2
| | |
PVC-0 PVC-1 PVC-2
The Shipment API uses a Deployment because every replica is interchangeable. Tracking and synchronization workers also use Deployments because queue messages can be processed by any healthy worker.
The event-storage system uses a StatefulSet because individual members require persistent identity and dedicated storage.
Deployment and Failure Flow
Suppose a new Shipment API version is released. The Deployment creates a new ReplicaSet and gradually shifts capacity from the old ReplicaSet to the new one.
- A new container image is applied to the Deployment.
- The Deployment creates a new ReplicaSet.
- New pods begin starting.
- Readiness checks prevent premature traffic.
- Ready new pods enter service.
- Old replicas are gradually removed.
- The previous ReplicaSet remains available as rollout history.
If one new replica repeatedly fails readiness, rollout progress can stall rather than immediately replacing all healthy old replicas. Monitoring rollout status is therefore as important as monitoring application errors.
A StatefulSet failure behaves differently. If store-1 fails, Kubernetes can recreate store-1 and reconnect its persistent storage. The storage application must then determine whether its local data is current enough to rejoin or whether synchronization is required.
During this recovery, several layers need monitoring:
- Deployment availability — available versus desired replicas.
- Rollout progress — whether new ReplicaSets successfully become ready.
- Pod restarts — crashes, OOM kills, and probe failures.
- Pending pods — insufficient compute or placement constraints.
- Persistent volume attachment — whether stateful replicas can recover their storage.
- Application replication lag — whether recovered stateful members are synchronized.
- Request latency and errors — whether capacity loss affects clients.
- Queue depth — whether worker capacity is keeping up with incoming events.
Scaling behavior also differs. Increasing Shipment API replicas from 8 to 16 is primarily a compute-capacity decision. Increasing the stateful storage system from 3 to 5 members may trigger replication, rebalancing, additional storage allocation, and changes in quorum or partition distribution.
The Kubernetes scaling command may look similar while the application-level consequences are completely different.
Common Mistakes
Controller problems in production usually come from choosing workload semantics that do not match application architecture or from assuming Kubernetes can solve application-level state management automatically.
| Mistake | Production Impact | Better Approach |
|---|---|---|
| Creating production pods directly | Failed or deleted pods are not maintained through a proper workload lifecycle. | Use an appropriate workload controller. |
| Managing ReplicaSets directly for normal APIs | Application rollout and revision management become unnecessarily manual. | Use a Deployment and let it manage ReplicaSets. |
| Using StatefulSet for every application that accesses data | Unnecessary identity, storage, and lifecycle complexity is introduced. | Keep application compute stateless when durable state can live externally. |
| Assuming StatefulSet manages database replication | Pods restart correctly while application data remains inconsistent or unavailable. | Design replication, quorum, synchronization, and recovery at the database layer. |
| Ignoring rollout capacity | Surge pods remain Pending and deployments stall. | Reserve capacity according to rollout strategy and failure requirements. |
| Deploying incompatible schema changes | Old and new replicas fail while serving traffic simultaneously. | Use backward-compatible expand-and-contract migrations. |
| Using weak readiness checks | New replicas receive traffic before they can serve requests reliably. | Make readiness reflect actual traffic-serving capability. |
| Assuming persistent storage equals high availability | A recovered disk may still contain stale, corrupt, or unavailable application state. | Combine storage durability with application-level replication and tested recovery. |
| Scaling StatefulSets without understanding rebalancing | New members create heavy network, disk, or replication load. | Model application-specific scaling and data movement before changing replica counts. |
| Ignoring controller status | Applications appear configured correctly while rollouts or reconciliation remain stuck. | Monitor desired, ready, available, updated, and unavailable replica states. |
Production Checklist
Controller configuration should reflect application semantics, expected failures, rollout behavior, and available infrastructure capacity.
- Use Deployments for interchangeable replicas. Keep normal APIs, frontends, and stateless workers independent from pod identity.
- Let Deployments own ReplicaSets. Avoid direct ReplicaSet management unless a specific architecture requires it.
- Use StatefulSets only for stable identity requirements. Confirm that ordinal identity or per-replica storage is actually necessary.
- Define rollout capacity. Ensure the cluster can satisfy maxSurge requirements during deployments.
- Protect minimum availability. Configure rollout behavior according to acceptable temporary capacity loss.
- Validate readiness semantics. New replicas should enter service only after application initialization is complete.
- Measure rollout duration. Track image pulls, startup, initialization, readiness, and termination time.
- Design version overlap. Ensure adjacent application versions can coexist during rolling releases.
- Plan stateful recovery. Document what happens after a StatefulSet replica reconnects to its persistent volume.
- Monitor replication separately. Kubernetes pod health does not prove database or broker replication health.
- Test node failures. Confirm both stateless replacement and stateful storage reattachment behavior.
- Monitor Pending replicas. Detect insufficient compute, storage, topology, or scheduling capacity quickly.
- Review scaling consequences. Treat stateful replica changes as data-architecture operations, not only Kubernetes operations.
- Test rollback behavior. Verify that application, configuration, database schema, and external contracts remain compatible with rollback.
Conclusion
Deployments, ReplicaSets, and StatefulSets solve different layers of Kubernetes workload management. ReplicaSets maintain replica counts, Deployments add stateless application lifecycle and rollout management, and StatefulSets provide stable identities and storage relationships for workloads that cannot treat replicas as anonymous.
The simplest production architecture is usually to keep compute stateless and use Deployments wherever possible. StatefulSets become valuable when distributed software genuinely depends on member identity or persistent per-replica storage, but they do not replace application-level replication, consistency, failover, or recovery mechanisms.
Key Takeaway: choose Kubernetes controllers based on the application's identity and state model. Deployments fit interchangeable compute; StatefulSets fit identity-sensitive workloads; ReplicaSets are normally the reconciliation layer underneath Deployments rather than the application-level abstraction.
Comments (0)