Kubernetes DaemonSet: One Pod Per Node Guide
Kubernetes DaemonSet runs one pod on each node. Official-style docs for log collectors, monitoring agents, tolerations, control-plane nodes, and updates.
π‘ Quick Answer: A DaemonSet ensures one pod runs on every node (or a subset). Define like a Deployment but with
kind: DaemonSetand noreplicas. Common uses: log collectors (Fluentd/Fluent Bit), monitoring agents (node-exporter, DCGM), CNI plugins (Calico, Cilium), and storage daemons (CSI node plugins). UsenodeSelectoror tolerations to target specific nodes.
The Problem
Some workloads must run on every node:
- Log collection β every node generates logs
- Metrics β node-level CPU/memory/disk monitoring
- Networking β CNI plugins, kube-proxy
- Security β runtime scanning, audit logging
- Storage β CSI node drivers, local volume provisioner
Deployments canβt guarantee one-per-node placement.
The Solution
Basic DaemonSet
apiVersion: apps/v1
kind: DaemonSet
metadata:
name: fluent-bit
namespace: logging
spec:
selector:
matchLabels:
app: fluent-bit
template:
metadata:
labels:
app: fluent-bit
spec:
containers:
- name: fluent-bit
image: fluent/fluent-bit:3.0
volumeMounts:
- name: varlog
mountPath: /var/log
readOnly: true
- name: containers
mountPath: /var/lib/docker/containers
readOnly: true
resources:
requests:
cpu: 50m
memory: 64Mi
limits:
cpu: 200m
memory: 128Mi
volumes:
- name: varlog
hostPath:
path: /var/log
- name: containers
hostPath:
path: /var/lib/docker/containers
tolerations:
- operator: Exists # Run on ALL nodes including taintedTarget Specific Nodes
spec:
template:
spec:
# Only GPU nodes
nodeSelector:
nvidia.com/gpu.present: "true"
# Or use affinity
affinity:
nodeAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
nodeSelectorTerms:
- matchExpressions:
- key: node-role
operator: In
values: ["worker", "gpu"]Run on Every Node Including Control Plane
By default, control-plane (master) nodes are tainted, so a DaemonSet skips them. To run one pod on every node β control plane included β tolerate the control-plane taints:
spec:
template:
spec:
tolerations:
# Run on control-plane / master nodes too
- key: node-role.kubernetes.io/control-plane
effect: NoSchedule
- key: node-role.kubernetes.io/master
effect: NoSchedule
containers:
- name: fluentd
image: fluent/fluentd:v1.16To tolerate all taints (truly one pod per node, no exceptions), use a single blanket toleration:
tolerations: [{operator: "Exists"}].
Update Strategy
spec:
updateStrategy:
type: RollingUpdate # Default
rollingUpdate:
maxUnavailable: 1 # Update 1 node at a time
maxSurge: 0 # No extra pods (K8s 1.22+)
# Or OnDelete β manual control
# updateStrategy:
# type: OnDelete
# Pods only update when manually deletedCommon DaemonSet Patterns
# Node Exporter (Prometheus monitoring)
apiVersion: apps/v1
kind: DaemonSet
metadata:
name: node-exporter
namespace: monitoring
spec:
selector:
matchLabels:
app: node-exporter
template:
metadata:
labels:
app: node-exporter
spec:
hostNetwork: true # Access host network metrics
hostPID: true # Access host processes
containers:
- name: node-exporter
image: prom/node-exporter:v1.8.0
ports:
- containerPort: 9100
hostPort: 9100
args:
- --path.procfs=/host/proc
- --path.sysfs=/host/sys
- --path.rootfs=/host/root
volumeMounts:
- name: proc
mountPath: /host/proc
readOnly: true
- name: sys
mountPath: /host/sys
readOnly: true
- name: root
mountPath: /host/root
readOnly: true
volumes:
- name: proc
hostPath:
path: /proc
- name: sys
hostPath:
path: /sys
- name: root
hostPath:
path: /
tolerations:
- operator: ExistsDaemonSet vs Deployment
| Feature | DaemonSet | Deployment |
|---|---|---|
| Replicas | 1 per node (automatic) | Fixed count |
| Scaling | Follows node count | Manual or HPA |
| Scheduling | Guaranteed per-node | Best-effort placement |
| Update | Rolling per-node | Rolling per-replica |
| Use case | Node agents | Application workloads |
Manage DaemonSets
# Check DaemonSet status
kubectl get daemonset -n logging
# NAME DESIRED CURRENT READY UP-TO-DATE AVAILABLE
# fluent-bit 5 5 5 5 5
# Rollout status
kubectl rollout status daemonset/fluent-bit -n logging
# Rollback
kubectl rollout undo daemonset/fluent-bit -n logging
# Restart all pods
kubectl rollout restart daemonset/fluent-bit -n loggingCommon Issues
DaemonSet pod not running on a node
Node is tainted and DaemonSet doesnβt have matching toleration. Add tolerations: [{operator: "Exists"}] to run on all nodes.
DESIRED shows fewer than total nodes
nodeSelector or affinity restricts which nodes get pods. Check: kubectl describe daemonset <name>.
DaemonSet using too much node resources
Set resource requests and limits. Use PriorityClass to ensure DaemonSet pods arenβt evicted before application pods.
Best Practices
- Always set resource requests/limits β DaemonSets run on every node, waste adds up
- Use
tolerations: [{operator: "Exists"}]for system DaemonSets β must run everywhere - RollingUpdate with
maxUnavailable: 1β safe default for production - Use
hostPathvolumes sparingly β security risk, prefer CSI drivers - Set
priorityClassName: system-node-criticalfor essential DaemonSets
FAQ
How do I make Kubernetes run one pod per node?
Use a DaemonSet. Kubernetes automatically schedules exactly one copy of the pod on each node that matches the DaemonSetβs nodeSelector/tolerations β and adds or removes pods as nodes join or leave. You do not set replicas; the node count determines the pod count.
What is the Kubernetes object that runs a copy of a pod on every node?
The DaemonSet (apps/v1). A DaemonSet ensures all (or a selected subset of) nodes run a copy of a pod. This is the official mechanism for node-level agents such as log collectors, monitoring exporters, CNI plugins, and CSI storage drivers.
Does a DaemonSet run on every node including control-plane nodes?
Not by default β control-plane nodes are tainted, so DaemonSet pods skip them. Add tolerations for node-role.kubernetes.io/control-plane (and the legacy master key), or use tolerations: [{operator: "Exists"}] to run on every node with no exceptions.
How do I run a DaemonSet on only some nodes?
Add a nodeSelector (e.g. nvidia.com/gpu.present: "true") or nodeAffinity to the pod template. The DaemonSet then places one pod only on nodes matching those rules β useful for GPU device plugins or storage daemons that belong on specific hardware.
Why does DESIRED show fewer pods than my node count?
A nodeSelector, affinity rule, or missing toleration is excluding nodes. Run kubectl describe daemonset <name> to see why pods arenβt scheduled, and confirm the DaemonSet tolerates any taints on the missing nodes.
Key Takeaways
- DaemonSets run exactly one pod per node (or subset with nodeSelector)
- Automatically adds/removes pods as nodes join/leave the cluster
- Essential for logging, monitoring, networking, and storage agents
- Use tolerations to ensure DaemonSets run on tainted nodes
- Rolling update strategy updates one node at a time for safety

Recommended
Kubernetes Recipes β The Complete Book100+ production-ready patterns with detailed explanations, best practices, and copy-paste YAML. Everything in one place.
Get the Book βLearn by Doing
CopyPasteLearn β Hands-on Cloud & DevOps CoursesMaster Kubernetes, Ansible, Terraform, and MLOps with interactive, copy-paste-run lessons. Start free.
Browse Courses βπ Deepen Your Skills β Hands-on Courses
Courses by CopyPasteLearn.com β Learn IT by Doing
