Runs exactly one Pod per node — the pattern for node-level agents.
You only need the cluster components from setup. This chapter observes existing DaemonSets in kube-system; it does not create new app resources.
A Deployment says "run N copies, anywhere there's room." A DaemonSet says something different: "run exactly one copy on every node." As nodes join the cluster, they automatically get the Pod; as they leave, it goes with them.
That's the right model for per-node agents — things that must touch each machine:
- log shippers (Fluent Bit, Filebeat)
- monitoring agents (node-exporter)
- networking — the CNI (flannel) itself runs as a DaemonSet
📝 Multi-node note: a DaemonSet places one Pod on every node. On this single-node cluster you'll see exactly one — picture it fanning out to every machine in a real cluster.
You don't need to create a DaemonSet to study one — your cluster already runs a couple in kube-system:
kubectl get daemonset -n kube-systemNAME DESIRED CURRENT READY UP-TO-DATE AVAILABLE NODE SELECTOR AGE
kube-flannel-ds 1 1 1 1 1 <none> 1h
kube-proxy 1 1 1 1 1 kubernetes.io/os=linux 1h
DESIRED 1 here is simply "1 node in the cluster". Add nodes and these counts rise to match — no config change needed. Open k9s and type :ds to watch them.
Why is
kube-proxya DaemonSet? Every node needs Service routing, so it's the textbook DaemonSet use case — one copy per node, always. The short version: a Service is how traffic finds a moving target of Pods, andkube-proxyis the piece on each node that makes that routing work.
- ✅ A per-node agent (logs, metrics, networking, storage).
- ❌ A regular app — use a Deployment. You want "N replicas for capacity/availability", not "one per machine".
A DaemonSet itself has no special power over taints — like any Pod, it's still blocked from a tainted node unless its template carries a matching toleration. kube-flannel-ds and kube-proxy show up even on this single control-plane node only because kubeadm's manifests for them explicitly include a toleration for the control-plane taint (and others) — it's not something the DaemonSet kind grants automatically. To restrict your own agent to a subset (say, only GPU nodes), add the same nodeSelector + tolerations pair from the scheduling chapter:
spec:
template:
spec:
nodeSelector:
gpu: "true"
tolerations:
- key: gpu
operator: Equal
value: "true"
effect: NoScheduleWithout the nodeSelector, the DaemonSet still spreads to every node that doesn't repel it; without the matching tolerations, it gets repelled by any node carrying that taint, GPU or not.
What happens when you change a DaemonSet's Pod template (e.g. bumping the image) depends on spec.updateStrategy.type:
RollingUpdate(the default) — old Pods are deleted and replaced node by node automatically, same idea as a Deployment rollout. This matches what you'd expect.OnDelete— the controller does nothing until you manually delete a Pod; the replacement then picks up the new template. Switching to this is how you get the "template changed but nothing happened" behavior, useful when you need to control exactly when each node's agent restarts.
- Set
resourcesrequests/limits — DaemonSet Pods run on every node, so a leak is multiplied across the fleet. - Scope with a
nodeSelector/tolerations if the agent should only run on some nodes (e.g. GPU nodes).
← Scheduling, Taints & Tolerations · ↑ Contents · Job & CronJob →