Kokil Thapa - Professional Web Developer in Nepal
Freelancer Web Developer in Nepal with 15+ Years of Experience

Kokil Thapa is an experienced full-stack web developer focused on building fast, secure, and scalable web applications. He helps businesses and individuals create SEO-friendly, user-focused digital platforms designed for long-term growth.

Deployments vs StatefulSets vs DaemonSets

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.

Three Kubernetes Workload ControllersDeploymentRandom pod namesAny pod, any requestRolling updatesStatefulSetStable ordinalsOrdered startupPVC per podDaemonSetOne pod per nodeNode-scoped agentsCluster-wide opsShared foundation: Pod spec, labels, probes, resourcesAll three are declared in YAML and reconciled by the control planeChoose by identity, storage, and placement — not by container image
Deployments vs StatefulSets vs DaemonSets — identity, scaling, and node placement at a glance

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.

CriteriaDeploymentStatefulSetDaemonSet
Pod namingRandom hash suffixStable ordinal (app-0)Node-linked name
Scaling modelreplicas: Nreplicas: N, orderedOne per node (auto)
StorageUsually none or sharedPVC per pod via volumeClaimTemplatesOften hostPath or local disk
Network identityService load-balances any podHeadless Service + stable DNSHost network optional
Update strategyRollingUpdate (default), RecreateRollingUpdate, ordered partitionRollingUpdate, maxUnavailable
Typical workloadsAPIs, web apps, workersMySQL, Redis, Kafka, etcdFluent Bit, node-exporter, CNI
VerdictDefault choice for statelessWhen identity mattersWhen 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.

Deployment Rolling Update SequenceManifestimage tag changeNew RSscale up podsReady probepass = trafficOld RSscale to 0Service routes to ready endpoints onlyPod v2-aPod v2-bPod v2-cv1 xmaxUnavailable: 0 keeps capacity during rollout
Deployment rolling update — new ReplicaSet grows while old pods drain after readiness checks pass

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
StatefulSet Stable Identity Modelapp-0PVC data-app-0app-1PVC data-app-1app-2PVC data-app-2Headless Service: app-0.svc, app-1.svc, app-2.svcOrdered startup: app-0 ready before app-1 startsReschedule keeps ordinal — pod-1 never becomes pod-7
StatefulSet pods retain ordinal names, dedicated PVCs, and stable DNS records across restarts

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.

DaemonSet: One Agent Per NodeNode Aagent podNode Bagent podNode Cagent podDaemonSet controllerNew node joins cluster → pod scheduled automaticallyNode drained → pod removed with nodeNot for app traffic — for infra and observability
DaemonSet ensures every eligible node runs the same infrastructure pod — logs, metrics, or CNI

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.

  1. Deployment for Laravel PHP-FPM, nginx, Horizon queue workers, and Sanctum API pods.
  2. StatefulSet or managed DB for MySQL/PostgreSQL when in-cluster; prefer managed RDS or Cloud SQL when ops team is small.
  3. 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

Deployments manage interchangeable pods with random name suffixes and fast, unordered scaling — ideal for stateless HTTP services. StatefulSets assign stable ordinals like mysql-0, bind a dedicated PVC to each pod, and start or stop pods in sequence. DaemonSets schedule exactly one pod per eligible node, automatically adding or removing copies as nodes join or leave the cluster.

Use a Deployment when any replica can replace any other without breaking clients or corrupting data. That covers Laravel PHP-FPM pods, nginx frontends, queue workers, and internal REST APIs. Session state belongs in Redis or the database, not on local pod disk. I've seen teams put a standard Laravel app in a StatefulSet and then wonder why scale-up takes minutes instead of seconds — you gain ordered startup overhead with no benefit.

Each pod gets a predictable hostname via a headless Service, so peers resolve each other through DNS like mysql-1.mysql-headless.default.svc.cluster.local. Storage arrives through volumeClaimTemplates, which create one PersistentVolumeClaim per pod at creation time. Delete pod mysql-0 and Kubernetes recreates it with the same ordinal and reattaches the same PVC. Scale-down removes highest ordinals first. Updates can canary a single pod using spec.updateStrategy.rollingUpdate.partition.

DaemonSets ignore replica counts in the usual sense. The scheduler places one pod on every node matching your node selector or tolerations. Add a node and a pod appears; drain a node and that pod goes away. Common workloads include log shippers like Fluent Bit, metrics exporters like node-exporter, storage CSI node plugins, and security agents. Without tolerations such as operator: Exists, DaemonSet pods may skip tainted or control-plane nodes and leave monitoring blind spots.

Most production stacks combine all three. Run Laravel PHP-FPM, nginx, Horizon queue workers, and Sanctum API pods as Deployments. Use a StatefulSet or a managed service for MySQL or PostgreSQL — prefer RDS or Cloud SQL when ops headcount is small. Ship logs and node metrics with a DaemonSet like Fluent Bit on every worker. User uploads belong in S3-compatible object storage, not on pod disk. That split mirrors how I structure enterprise application deployments for clients with limited ops capacity.

Yes, but pod identity is not guaranteed. Any pod can mount a shared ReadWriteMany volume, or each pod can claim a PVC not tied to a specific ordinal. When a pod is deleted and recreated with a new random suffix name, volume binding depends on your storage class and reclaim policy. For data that must follow a specific pod identity, use a StatefulSet with volumeClaimTemplates instead.

Kubernetes recreates the pod with the same ordinal name and reattaches its existing PVC if the claim still exists. That stable identity across restarts 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 before any destructive operation.

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 automatically. You would never run a customer-facing API as a DaemonSet — you get one copy per node regardless of load, wasting resources on small clusters and capping density oddly on large ones.

Three questions cover most cases. Can you wipe the pod and lose nothing? Use a Deployment. Does the pod need a stable name and its own disk? Use a StatefulSet. Must it run on every node? Use a DaemonSet. For one-off admin tasks, reach for a Job or CronJob instead. Pick the controller first, then write the manifest — the wrong choice often surfaces months later as data loss or failed rollouts.

Run MySQL in a StatefulSet when you must host it inside the cluster, because each replica needs a stable ordinal, dedicated PVC, and predictable DNS for peer discovery. Do not put MySQL in a Deployment — pods get random names and no guaranteed volume identity. For small teams, managed databases like RDS or Cloud SQL often beat self-hosted StatefulSets. Keep Laravel in Deployments and MySQL in a managed service unless you accept the full ops burden.

A standard Laravel app has no need for stable pod ordinals, ordered startup, or per-pod PVCs. StatefulSets add sequential scale-up and scale-down overhead that slows horizontal scaling without delivering any benefit. Session and cache state belong in Redis or the database. Config comes from env vars or ConfigMaps. Treat Laravel PHP-FPM pods as cattle in a Deployment with a ClusterIP or Ingress Service in front — clients hit the Service VIP and never care which pod answers.

A headless Service sets clusterIP: None, so Kubernetes creates DNS A records pointing directly to individual pod IPs rather than a single virtual IP. StatefulSets reference this via spec.serviceName, giving each pod a stable hostname like mysql-0.mysql-headless.default.svc.cluster.local. That predictable network identity matters for MySQL replication, Redis Sentinel, Kafka brokers, and any clustered software where peers must discover each other by name.

Run anything that must touch every node: log shippers such as Fluent Bit or Filebeat, metrics exporters like node-exporter, storage CSI node plugins, CNI plugins, and security agents. DaemonSets use hostPath volumes to read node-level paths like /var/log. Pair them with Prometheus Alertmanager so a missing node-exporter pod triggers an alert instead of a silent monitoring gap. These are node-local infrastructure agents, not horizontally scaled application tiers.

Prefer managed RDS, Cloud SQL, or Elasticache when your ops team is small and you cannot babysit ordered rollouts, PVC reclaim policies, and backup drills yourself. StatefulSets shine when you must run data-plane software inside the cluster and accept that burden. On client projects with limited headcount, I keep MySQL and Redis in managed services and Laravel in Deployments — that split reduces the risk of data loss from a misconfigured cascade delete or botched partition rollout.

Budget Rs 15,000–40,000 per month (~USD 110–295) for small-cluster babysitting in Nepal, depending on SLA depth and on-call coverage. That gap between a cluster that works and one that survives traffic spikes is where ongoing Linux administration and support contracts matter. Be honest about team size — a three-person agency shipping Laravel 12 on PHP 8.3 may not need Kubernetes yet, while a multi-service product with compliance logging often does.

Share this article

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.

Quick Contact Options
Choose how you want to connect me: