
September 10, 2026
13 min read
By Kokil Thapa | Last reviewed: September 2026
You picked Kubernetes to run production apps, then hit three workload controllers that sound interchangeable. Deployments vs StatefulSets vs DaemonSets is the first architecture fork most teams get wrong. A Deployment treats pods as disposable. A StatefulSet gives each pod a stable name and disk. A DaemonSet puts one copy on every node. This guide maps each controller to real workloads, with copy-paste YAML and a decision table you can use in your next cluster review. If you ship PHP or Laravel stacks, see our Laravel production deployment checklist for the app layer that sits above these primitives.
What is the difference between Deployments, StatefulSets, and DaemonSets?
Kubernetes does not run containers directly. It runs pods, and workload controllers keep those pods at the shape you declare. Three controllers cover most production patterns. Understanding Deployments vs StatefulSets vs DaemonSets starts with what each one guarantees about pod identity, scaling, and placement.
A Deployment manages ReplicaSets. Pods get random suffix names like nginx-7d4f8b9c-xk2lm. Any pod can serve any request. When you scale or roll out, old pods terminate and new ones appear without fixed order. That is ideal for stateless HTTP services.
A StatefulSet assigns each pod a permanent ordinal: mysql-0, mysql-1, mysql-2. DNS records and PVC bindings follow that identity. Pods start and stop in sequence. If mysql-1 dies, it comes back as mysql-1 with the same volume claim.
A DaemonSet ignores replica counts in the usual sense. It schedules one pod per eligible node. Add a node, get a pod. Drain a node, lose that pod. You use this for infrastructure that must touch every machine.
The official Kubernetes docs describe Deployments as the standard way to run stateless applications. StatefulSets exist for apps that need persistent identity. DaemonSets cover per-node daemons. Those three sentences sound simple. Production gets messy when a team puts MySQL in a Deployment or runs a Laravel API as a StatefulSet without a reason.
| Criteria | Deployment | StatefulSet | DaemonSet |
|---|---|---|---|
| Pod naming | Random hash suffix | Stable ordinal (app-0) | Node-linked name |
| Scaling model | replicas: N | replicas: N, ordered | One per node (auto) |
| Storage | Usually none or shared | PVC per pod via volumeClaimTemplates | Often hostPath or local disk |
| Network identity | Service load-balances any pod | Headless Service + stable DNS | Host network optional |
| Update strategy | RollingUpdate (default), Recreate | RollingUpdate, ordered partition | RollingUpdate, maxUnavailable |
| Typical workloads | APIs, web apps, workers | MySQL, Redis, Kafka, etcd | Fluent Bit, node-exporter, CNI |
| Verdict | Default choice for stateless | When identity matters | When every node needs an agent |
On teams I work with, the wrong controller choice shows up months later as data loss or failed rollouts. Pick the controller first. Then write the manifest. Our GitOps with Argo CD guide shows how to keep those manifests under version control.
When should you use a Deployment instead of a StatefulSet?
Reach for a Deployment when any replica can replace any other replica without breaking clients or corrupting data. That covers most Laravel PHP-FPM pods, nginx frontends, queue workers, and internal REST APIs. The pod is cattle, not a pet.
Deployments support declarative rollouts. You change the image tag or env var. Kubernetes creates a new ReplicaSet, gradually shifts traffic, and keeps old pods until the new ones pass readiness probes. That pairs well with CI/CD blue-green patterns at the ingress layer.
Minimal Deployment manifest
apiVersion: apps/v1
kind: Deployment
metadata:
name: laravel-api
labels:
app: laravel-api
spec:
replicas: 3
selector:
matchLabels:
app: laravel-api
strategy:
type: RollingUpdate
rollingUpdate:
maxSurge: 1
maxUnavailable: 0
template:
metadata:
labels:
app: laravel-api
spec:
containers:
- name: php-fpm
image: registry.example.com/laravel-api:2026.09.1
ports:
- containerPort: 9000
readinessProbe:
httpGet:
path: /health
port: 8080
initialDelaySeconds: 10
periodSeconds: 5
resources:
requests:
cpu: 250m
memory: 512Mi
limits:
cpu: "1"
memory: 1Gi Pair that Deployment with a ClusterIP or Ingress Service. Clients hit the Service VIP. They never care which pod answers. Session state belongs in Redis or the database, not on local disk inside the pod.
Do not use a StatefulSet for a standard Laravel app just because you want three replicas. You gain ordered startup overhead without benefit. I've seen teams do this and then wonder why scale-up takes minutes instead of seconds.
Use a Deployment when:
- Pods share no local state that other pods cannot rebuild.
- Horizontal scaling should be fast and unordered.
- You want simple rollbacks via
kubectl rollout undo. - Your app reads config from env vars or ConfigMaps, not local disk identity.
For PHP apps still on bare VPS, zero-downtime Deployer releases solve a similar problem outside Kubernetes. The mental model transfers: new instances become ready, then old ones retire.
How do StatefulSets handle persistent identity and storage?
StatefulSets solve problems Deployments cannot. Each pod gets a predictable hostname inside a headless Service. Peer discovery for clustered software becomes DNS lookups like mysql-1.mysql-headless.default.svc.cluster.local. That matters for MySQL replication, Redis Sentinel, or Kafka brokers where peers must know who is who.
Storage arrives through volumeClaimTemplates. Kubernetes creates one PersistentVolumeClaim per pod at creation time. Delete pod mysql-0, recreate it, and it reattaches the same PVC. Delete the StatefulSet with cascade policy wrong, and you can orphan or wipe data. Read the Kubernetes StatefulSet documentation before your first production cutover.
StatefulSet with headless Service
apiVersion: v1
kind: Service
metadata:
name: mysql-headless
spec:
clusterIP: None
selector:
app: mysql
ports:
- port: 3306
---
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: mysql
spec:
serviceName: mysql-headless
replicas: 3
podManagementPolicy: OrderedReady
selector:
matchLabels:
app: mysql
template:
metadata:
labels:
app: mysql
spec:
containers:
- name: mysql
image: mysql:8.4
ports:
- containerPort: 3306
volumeMounts:
- name: data
mountPath: /var/lib/mysql
volumeClaimTemplates:
- metadata:
name: data
spec:
accessModes: ["ReadWriteOnce"]
storageClassName: gp3
resources:
requests:
storage: 50Gi Operational rules differ from Deployments. Scale-down removes highest ordinals first. Updates can use a partition to canary one pod: set spec.updateStrategy.rollingUpdate.partition: 2 and only app-2 gets the new spec until you lower the partition. That is slower but safer for databases.
Managed databases (RDS, Cloud SQL) often beat self-hosted StatefulSets for small teams. StatefulSets shine when you must run data plane software inside the cluster and accept the ops burden. For app tiers, keep MySQL in a managed service and Laravel in Deployments. That split mirrors how I structure enterprise application deployments for clients with limited ops headcount.
How do DaemonSets run one pod per node?
DaemonSets answer a different question: "What must run on every node?" They ignore your desired replica count in practice. The scheduler places one pod per node that matches the node selector or tolerations you define.
Common DaemonSet workloads include log shippers (Fluent Bit, Filebeat), metrics exporters (node-exporter), storage drivers (CSI node plugins), and security agents. The cluster autoscaler adds nodes; DaemonSets appear automatically. No one updates a replica count manually.
DaemonSet for node logging agent
apiVersion: apps/v1
kind: DaemonSet
metadata:
name: fluent-bit
namespace: logging
spec:
selector:
matchLabels:
app: fluent-bit
template:
metadata:
labels:
app: fluent-bit
spec:
tolerations:
- operator: Exists
containers:
- name: fluent-bit
image: fluent/fluent-bit:3.2
volumeMounts:
- name: varlog
mountPath: /var/log
readOnly: true
- name: containers
mountPath: /var/lib/docker/containers
readOnly: true
volumes:
- name: varlog
hostPath:
path: /var/log
- name: containers
hostPath:
path: /var/lib/docker/containers Tolerations matter. Without operator: Exists, DaemonSet pods may skip control-plane or tainted nodes. That leaves blind spots in monitoring. Pair DaemonSets with Prometheus Alertmanager alerting so a missing node-exporter pod triggers a page, not a silent gap.
Do not run your customer-facing API as a DaemonSet. You would get one copy per node regardless of load. That wastes resources on small clusters and caps density oddly on large ones. DaemonSets are for node-local infrastructure, not horizontally scaled application tiers.
Which Kubernetes workload fits Laravel, databases, and mixed stacks?
Most production stacks combine all three controllers. The pattern repeats across client projects I help migrate from single VPS hosts to Kubernetes or hybrid setups.
- Deployment for Laravel PHP-FPM, nginx, Horizon queue workers, and Sanctum API pods.
- StatefulSet or managed DB for MySQL/PostgreSQL when in-cluster; prefer managed RDS or Cloud SQL when ops team is small.
- DaemonSet for log shipping and node metrics on every worker.
A legal-tech portal with document uploads might run Laravel in Deployments, Redis as a StatefulSet (or Elasticache), and Fluent Bit as a DaemonSet. User files live in object storage (S3-compatible), not on pod disk. That matches how document-heavy apps like those in our Mijar Law Associates portfolio case should be architected for scale.
Validate YAML before apply. A typo in volumeClaimTemplates is expensive. Use the JSON and YAML formatter tool to catch indentation errors locally. For rollback drills, read how to roll back a failed deployment safely — the kubectl commands differ slightly per controller type.
Quick decision checklist
- Can you wipe the pod and lose nothing? → Deployment.
- Does the pod need a stable name and its own disk? → StatefulSet.
- Must it run on every node? → DaemonSet.
- Is it a one-off admin job? → Job or CronJob (outside this comparison).
Version skew matters in 2026. Run a supported Kubernetes release on your provider. Pin container images by digest or semver tag, not :latest. The Deployment controller docs and DaemonSet docs stay the authoritative reference when behaviour shifts between minor versions.
Teams moving from Ansible-managed VPS fleets can reuse playbooks for node bootstrap, then hand app lifecycle to Kubernetes. Our Ansible playbooks for PHP provisioning cover the pre-Kubernetes path. Multi-region deployment adds another layer: Deployments replicate easily across regions; StatefulSets need careful storage replication or regional managed services.
For ongoing ops, Linux system administration and support and maintenance contracts often cover the gap between "cluster works" and "cluster survives Dashain traffic spikes." Budget Rs 15,000–40,000/month (~USD 110–295) for small-cluster babysitting in Nepal, depending on SLA and on-call depth.
Compare rollout strategies at the pipeline level in blue-green vs canary deployments. Kubernetes Deployments give you rolling updates natively. Canary traffic splitting usually lives in Ingress or a service mesh, not in the workload controller itself.
If you are still choosing whether Kubernetes beats symlink Deployer releases on a single Ubuntu box, be honest about team size. A three-person agency shipping Laravel 12 on PHP 8.3 may not need Kubernetes yet. A product with multiple services, background workers, and compliance logging often does. Read more about that trade-off on the about page and the main services overview.
Key Takeaways
- Deployments are the default for stateless apps — Laravel APIs, nginx, and queue workers belong here.
- StatefulSets give stable pod names, ordered lifecycle, and per-pod PVCs — use them for clustered or disk-bound software.
- DaemonSets run one infrastructure pod per node — logging, metrics, and CNI plugins, not customer traffic.
- Never store session or upload data on Deployment pod disks; use Redis, object storage, or managed databases.
- Validate manifests, test rollbacks per controller type, and prefer managed databases when ops headcount is thin.
- Most real stacks combine all three controllers plus Jobs, Ingress, and external managed services.
People Also Ask
Can a Deployment use persistent volumes?
Yes, but any pod can mount a shared ReadWriteMany volume, or each pod can claim a PVC that is not tied to identity. When the pod is deleted and recreated with a new name, the volume binding behaviour depends on your storage class and reclaim policy. For data that must follow a specific pod ordinal, use a StatefulSet with volumeClaimTemplates instead.
What happens if you delete a StatefulSet pod?
Kubernetes recreates the pod with the same ordinal name and reattaches its existing PVC if the claim still exists. That is the core promise of StatefulSets. Deleting the StatefulSet object itself is dangerous — use kubectl delete statefulset mysql --cascade=orphan if you need to keep pods running during migration, and always confirm backup status first.
How is a DaemonSet different from a Deployment with replicas equal to node count?
A Deployment with N replicas does not guarantee one pod per node. The scheduler may stack multiple pods on one node and leave another empty. A DaemonSet enforces per-node placement and automatically adjusts when nodes join or leave the cluster. For node-local agents, that guarantee is the whole point.
Which controller supports rolling updates with zero downtime?
Deployments support rolling updates with maxUnavailable: 0 and readiness probes — the standard pattern for zero-downtime app releases. StatefulSets support ordered rolling updates but are slower because of pod identity and storage. DaemonSets roll node by node; cluster-wide app traffic should not depend on DaemonSet update timing.
Pick the right controller before you write YAML
Deployments vs StatefulSets vs DaemonSets is not a popularity contest. It is a question about identity, storage, and placement. Default to Deployments for stateless tiers. Reach for StatefulSets only when stable names and disks are non-negotiable. Reserve DaemonSets for per-node infrastructure. Get that split right and your rollouts, backups, and scaling policies become boring — which is exactly what production should feel like.
Need help mapping a Laravel or multi-service stack onto Kubernetes, or deciding if you should stay on VPS Deployer releases for now? Contact us for a practical architecture review. You can also explore feature-branch deployment workflows and production booking platforms we have shipped for examples of apps that demand reliable release mechanics.
Frequently Asked Questions
0 Comments
Leave a comment
Your email is not published. Comments appear once they have been read. Sign in to have your details filled in.

