Some workloads need to run on every node (or every node matching a selector) — not scaled by replica count, but by node count. Log collectors, monitoring agents, network plugins, storage drivers — these are DaemonSet territory.

1. What a DaemonSet Does

A DaemonSet ensures that exactly one Pod runs on each eligible node. When a node joins the cluster, the DaemonSet automatically schedules a Pod on it. When a node is removed, the Pod is garbage-collected.

DaemonSet: fluentd worker-1 fluentd-xk2j9 worker-2 fluentd-abc12 worker-3 fluentd-def34 master-1 ❌ (taint blocks)

Common DaemonSet Use Cases

CategoryExamples
LoggingFluentd, Fluent Bit, Filebeat — collect logs from every node
MonitoringPrometheus Node Exporter, Datadog Agent — node-level metrics
Networkingkube-proxy, Calico, Cilium — CNI plugins that configure every node
StorageCSI node plugins — mount volumes on each node
SecurityFalco — runtime security monitoring on every node

DaemonSet Manifest

apiVersion: apps/v1
kind: DaemonSet
metadata:
  name: fluentd
  namespace: kube-system
spec:
  selector:
    matchLabels:
      app: fluentd
  template:
    metadata:
      labels:
        app: fluentd
    spec:
      containers:
        - name: fluentd
          image: fluent/fluentd:v1.16
          resources:
            requests:
              cpu: 100m
              memory: 200Mi
            limits:
              memory: 500Mi
          volumeMounts:
            - name: varlog
              mountPath: /var/log
            - name: containers
              mountPath: /var/lib/docker/containers
              readOnly: true
      volumes:
        - name: varlog
          hostPath:
            path: /var/log
        - name: containers
          hostPath:
            path: /var/lib/docker/containers
No replicas field. Unlike Deployments, DaemonSets don't have a replica count. The number of Pods is determined by the number of matching nodes. You control which nodes via nodeSelector, nodeAffinity, and tolerations.

2. Controlling Which Nodes Get a Pod

nodeSelector — Simple Label Matching

# Only schedule on nodes with label "disk=ssd":
spec:
  template:
    spec:
      nodeSelector:
        disk: ssd

nodeAffinity — Advanced Rules

# Only schedule on nodes in specific zones:
spec:
  template:
    spec:
      affinity:
        nodeAffinity:
          requiredDuringSchedulingIgnoredDuringExecution:
            nodeSelectorTerms:
              - matchExpressions:
                  - key: topology.kubernetes.io/zone
                    operator: In
                    values: [us-east-1a, us-east-1b]

Tolerations — Running on Control Plane Nodes

Control plane nodes have taints that prevent scheduling. To run a DaemonSet on them (e.g., for monitoring), add tolerations:

# Tolerate control plane taint:
spec:
  template:
    spec:
      tolerations:
        - key: node-role.kubernetes.io/control-plane
          operator: Exists
          effect: NoSchedule
        # For older clusters:
        - key: node-role.kubernetes.io/master
          operator: Exists
          effect: NoSchedule
# Tolerate ALL taints (run on every node no matter what):
      tolerations:
        - operator: Exists    # matches any key, any value, any effect
DaemonSets bypass the scheduler by default (pre-1.12). In modern K8s (1.12+), DaemonSet Pods go through the normal scheduler. But DaemonSets automatically add tolerations for node.kubernetes.io/unschedulable and node.kubernetes.io/not-ready — so they still schedule on cordoned or not-ready nodes. This ensures system-critical agents keep running.
CNI plugins (Calico, Cilium) and kube-proxy run as DaemonSets with tolerations for ALL taints. They must run on every node — including cordoned nodes during drain operations — because without networking, nothing else works. Check: kubectl get ds -n kube-system.

3. Update Strategies

StrategyBehaviorUse Case
RollingUpdate (default)Kill old Pod, start new Pod — one node at a timeMost DaemonSets (logging, monitoring)
OnDeleteOnly updates Pods when manually deletedCritical system agents where you control timing

RollingUpdate Parameters

spec:
  updateStrategy:
    type: RollingUpdate
    rollingUpdate:
      maxUnavailable: 1        # How many nodes can lose their DS Pod at once
      maxSurge: 0              # (K8s 1.22+) Extra Pods allowed during update
ConfigBehaviorTrade-off
maxUnavailable: 1, maxSurge: 0Delete old, then create new (one at a time)Brief gap — no Pod on that node during transition
maxUnavailable: 0, maxSurge: 1Create new, wait until Ready, then delete oldZero-gap but temporarily 2 Pods on one node
maxUnavailable: "10%"Update 10% of nodes simultaneouslyFaster rollout, higher blast radius
For CNI plugin DaemonSets, use maxUnavailable: 1 with maxSurge: 0 carefully — during the transition, that node briefly has no networking plugin. For logging agents, maxSurge: 1 is safer (no log gap). For critical infrastructure, OnDelete gives you full control over rollout timing.

Checking Rollout Status

# Watch DaemonSet rollout:
kubectl rollout status daemonset/fluentd -n kube-system

# See current state:
kubectl get ds fluentd -n kube-system
# NAME      DESIRED   CURRENT   READY   UP-TO-DATE   AVAILABLE   AGE
# fluentd   3         3         3       3            3           5d

# DESIRED = number of eligible nodes
# UP-TO-DATE = Pods running latest template
# If UP-TO-DATE < DESIRED, rollout is in progress

4. Practical Patterns

Host-Level Access

DaemonSet Pods often need access to the host's filesystem, network, or devices:

spec:
  template:
    spec:
      # Use host network (share the node's IP):
      hostNetwork: true
      
      # Use host PID namespace (see all processes on node):
      hostPID: true
      
      # Mount host directories:
      volumes:
        - name: host-root
          hostPath:
            path: /
        - name: host-proc
          hostPath:
            path: /proc
      
      # Required for some agents (e.g., Falco needs kernel access):
      containers:
        - name: agent
          securityContext:
            privileged: true       # ⚠️ Use only when necessary
DaemonSets requiring privileged: true or hostNetwork are security-sensitive. In CKS context: these should be restricted to specific namespaces (kube-system), governed by Pod Security Standards, and the images should be from trusted registries only. Audit them carefully.

Resource Reservation

DaemonSet Pods consume resources on every node. Plan for this:

# If your DS requests 200Mi memory on each node,
# and you have 100 nodes, that's 20Gi of memory reserved cluster-wide.
# This reduces allocatable capacity for other workloads.

# Check DS resource footprint:
kubectl get ds -A -o custom-columns=\
NAME:.metadata.name,\
NS:.metadata.namespace,\
CPU_REQ:.spec.template.spec.containers[0].resources.requests.cpu,\
MEM_REQ:.spec.template.spec.containers[0].resources.requests.memory

DaemonSet vs Static Pods

DaemonSetStatic Pod
Managed byDaemonSet controller (API server)kubelet directly (manifest on disk)
Visible in API✅ Full CRUDMirror Pod (read-only)
UpdatesRolling update strategyModify file on disk → kubelet restarts
Use caseUser workloads (logging, monitoring)Bootstrap (etcd, API server, scheduler)
Node selectionnodeSelector, affinity, tolerationsRuns only on the node where the file exists
CKA exam: know the difference between DaemonSets and Static Pods. Control plane components (etcd, kube-apiserver, kube-scheduler, kube-controller-manager) are Static Pods — their manifests live in /etc/kubernetes/manifests/. kube-proxy and CNI plugins are DaemonSets.

5. DaemonSet vs Deployment: When Which?

NeedUseWhy
One Pod per node (infrastructure agent)DaemonSetAutomatic: new node → new Pod
Exactly N replicas regardless of nodesDeploymentReplica count is independent of cluster size
Host-level access (hostPath, hostNetwork)DaemonSetMakes sense per-node (monitoring, networking)
Scale independently of infrastructureDeployment + HPAApplication workloads

Summary

ConceptKey Point
DaemonSetEnsures one Pod on every (or selected) node
No replicas fieldPod count = eligible node count
Targeting nodesnodeSelector, nodeAffinity, tolerations
Control plane nodesNeed explicit toleration for node-role.kubernetes.io/control-plane:NoSchedule
RollingUpdatemaxUnavailable controls how many nodes lose a Pod simultaneously
maxSurge (1.22+)Allows new Pod before old is deleted (zero-gap updates)
OnDeleteManual control — Pod only updates when you delete it
Common useskube-proxy, CNI, log collectors, monitoring agents, security agents

📝 Quiz: DaemonSets

Q1: You have a 5-node cluster (3 workers + 2 control plane). A DaemonSet has no tolerations. How many Pods does it run?

3 Pods (one on each worker). Control plane nodes have a node-role.kubernetes.io/control-plane:NoSchedule taint. Without a matching toleration, DaemonSet Pods don't schedule there.

Q2: How do you make a DaemonSet run on ALL nodes including control plane?

Add a toleration for the control plane taint:
tolerations:
- key: node-role.kubernetes.io/control-plane
operator: Exists
effect: NoSchedule

Or use - operator: Exists (tolerates all taints).

Q3: A new worker node joins the cluster. What happens to existing DaemonSets?

Each DaemonSet whose node selector/affinity matches the new node automatically creates a Pod on it. The DaemonSet controller watches for new nodes and reconciles. No manual action needed.

Q4: What's the difference between maxUnavailable: 1, maxSurge: 0 and maxUnavailable: 0, maxSurge: 1 for a DaemonSet update?

maxUnavailable:1, maxSurge:0: Old Pod is deleted first, then new Pod starts. Brief gap with no Pod on that node.
maxUnavailable:0, maxSurge:1: New Pod starts first (temporarily 2 Pods on one node), once Ready the old Pod is deleted. Zero-gap but needs extra resources per node during transition.

Q5: kube-proxy runs as a DaemonSet but etcd runs as a Static Pod. Why the difference?

etcd must run before the API server is available (it's the API server's backend). Since DaemonSets are managed through the API server, etcd can't be a DaemonSet — it's a bootstrap dependency. kube-proxy runs after the cluster is up, so it can be managed normally through the API as a DaemonSet.

Q6: You want a logging DaemonSet to run only on nodes labeled environment=production. A new node without this label joins. Does it get a Pod?

No. If the DaemonSet has nodeSelector: {environment: production}, only nodes with that label are eligible. The new unlabeled node doesn't match, so no Pod is created. If you later add the label to the node, the Pod will be created automatically.