When do you use StatefulSet vs Deployment vs DaemonSet, and what makes StatefulSet special?
Quick Answer
Use Deployments for stateless apps that can scale freely, StatefulSets for workloads needing stable network identity and persistent storage (databases, Kafka), and DaemonSets for node-level agents that must run exactly once per node (log collectors, monitoring). StatefulSets provide ordered startup, stable pod names, and dedicated persistent volumes per replica.
Detailed Answer
Think of three different staffing models at a company. A Deployment is like a pool of identical customer service agents where any agent can handle any call and they are interchangeable. A StatefulSet is like a team of specialists where each person has their own desk, their own filing cabinet, and clients who always ask for them by name. A DaemonSet is like having one security guard assigned to every floor of the building, no more and no less, regardless of how many floors exist.
A Deployment manages stateless pods through a ReplicaSet. Pods get random names like payments-api-7f8d9c-x4k2q and can be killed and recreated freely because they hold no unique state. Scaling is trivial because every replica is identical. Rolling updates replace pods one by one without caring about order. This makes Deployments ideal for web servers, APIs, workers processing from queues, and any application where one replica can substitute for another.
A StatefulSet manages stateful pods with guarantees that Deployments cannot provide. Each pod gets a stable, predictable hostname like payments-db-0, payments-db-1, payments-db-2. These names persist across restarts and rescheduling. Each pod gets its own PersistentVolumeClaim through volumeClaimTemplates, so payments-db-0 always mounts the same volume even after rescheduling to a different node. Pods are created in order (0 before 1 before 2) and terminated in reverse order. This ordered lifecycle is critical for distributed systems like PostgreSQL with streaming replication, Kafka brokers that need stable broker IDs, or ZooKeeper nodes that form a quorum. The headless Service associated with a StatefulSet creates DNS records like payments-db-0.payments-db-headless.payments.svc.cluster.local for direct pod addressing.
A DaemonSet ensures exactly one pod runs on every node (or a subset of nodes matching a nodeSelector or affinity). When a new node joins the cluster, the DaemonSet controller automatically schedules a pod on it. When a node is removed, the pod is garbage collected. This makes DaemonSets perfect for node-level concerns: Fluentd or Filebeat for log collection, Prometheus node-exporter for host metrics, Datadog agents, CNI plugins, or storage drivers that need to run on every machine.
In production, choosing the wrong controller causes real problems. Running a database as a Deployment means all replicas share no stable identity, making replication configuration fragile and data loss likely during rescheduling. Running a stateless API as a StatefulSet adds unnecessary ordering constraints that slow scaling and complicate rollouts. Using a Deployment for a log collector means some nodes might run zero or multiple collectors depending on scheduling.
The non-obvious gotcha with StatefulSets is that deleting a StatefulSet does not delete the PVCs it created. This is intentional to prevent data loss, but it means orphaned volumes accumulate cost. Another surprise is that StatefulSet rolling updates proceed one pod at a time in reverse ordinal order, and if pod N-1 fails its readiness check, the rollout halts entirely. Teams must monitor StatefulSet rollouts carefully because a single unhealthy replica blocks the entire update.