When you create a Deployment, you tell Kubernetes "I want 3 replicas" and Kubernetes picks which nodes to put them on. But some things should not run on just 3 nodes — they need to run on every single node, always. That is what DaemonSets are for.
Think about log collection. Every node in your cluster is running Pods, and every Pod produces logs. If your log collector only runs on 3 out of 10 nodes, you are missing logs from 7 nodes entirely. You need one log collector sitting on every node, watching everything happening there.
A Deployment cannot guarantee this — it just spreads replicas wherever there is space. A DaemonSet guarantees exactly one Pod per node, no matter what.
Normal Deployment ("I want 3 replicas"):
Node 1 [ Pod ]
Node 2 [ Pod ]
Node 3 [ Pod ]
Node 4 (empty — Deployment doesn't care)
Node 5 (empty — Deployment doesn't care)
DaemonSet ("one Pod on EVERY node, always"):
Node 1 [ Pod ]
Node 2 [ Pod ]
Node 3 [ Pod ]
Node 4 [ Pod ] <- automatically added
Node 5 [ Pod ] <- automatically added
And it stays in sync automatically:
Cluster has 10 nodes -> DaemonSet runs 10 Pods
New node joins -> DaemonSet adds 1 Pod to it (11 total)
A node is removed -> That Pod is cleaned up (10 total)
You never have to manually adjust anything. The DaemonSet tracks the cluster and self-manages.
DaemonSets are always for infrastructure-level tasks — things that need visibility into a specific node, not just anywhere in the cluster.
| Use Case | Tool | Why Every Node |
|---|---|---|
| Log collection | Fluentd, Filebeat | Logs are written to each node's disk — collector must be local |
| Node monitoring | Prometheus Node Exporter | CPU, memory, disk metrics are node-specific |
| Networking | CNI plugins (Calico, Flannel) | Each node needs its own network agent to route traffic |
| Storage | CSI drivers | Storage plugins must run where the volumes are mounted |
| Security | Falco, intrusion detection agents | Threat detection needs to watch every node's syscalls |
A good rule of thumb: if the task needs to see what is happening on a specific machine, it belongs in a DaemonSet.
When you create a DaemonSet, the Kubernetes control plane watches the list of nodes. For every node that exists and does not already have a Pod from this DaemonSet, it creates one. The DaemonSet controller keeps checking — if a node gets added, it acts. If a node gets removed, it cleans up.
DaemonSet Controller loop (runs constantly):
Get list of all nodes
For each node:
Is there already a DaemonSet Pod here? -> do nothing
No Pod here? -> create one
Node joins cluster -> Pod created automatically
Node leaves cluster -> Pod cleaned up automatically
This is different from a Deployment, which just says "give me N Pods somewhere" and does not think in terms of nodes at all.
apiVersion: apps/v1
kind: DaemonSet
metadata:
name: log-collector
namespace: kube-system # infrastructure tools usually go here
spec:
selector:
matchLabels:
app: log-collector
template:
metadata:
labels:
app: log-collector
spec:
containers:
- name: fluentd
image: fluentd:v1.16
volumeMounts:
- name: varlog
mountPath: /var/log # read logs from the node's /var/log
- name: varlibdockercontainers
mountPath: /var/lib/docker/containers
readOnly: true
volumes:
- name: varlog
hostPath:
path: /var/log # mount the actual node's filesystem
- name: varlibdockercontainers
hostPath:
path: /var/lib/docker/containers
The key here is hostPath — the DaemonSet Pod mounts a folder directly from the node's filesystem. This is how it reads logs that other Pods wrote to disk. A regular Deployment Pod cannot do this reliably because it might be on a different node than the logs it needs to read.