# DaemonSet Not Fully Scheduled

> A DaemonSet should have exactly 1 pod per eligible node. When scheduled is below desired, some nodes are running without it, and because DaemonSets are usually logging, monitoring or security agents, those nodes are silently unobserved rather than merely degraded.

Source: https://zop.dev/integrations/kubernetes/recommendations/daemonset-not-fully-scheduled
Updated: 2026-08-19

---

## The gap is a coverage gap, not a capacity one

For a Deployment, missing replicas means less capacity. For a DaemonSet it means specific nodes
lack the agent entirely.

That distinction matters because of what DaemonSets typically are: log shippers, metrics agents,
security sensors, CNI components. A node missing its log shipper is not producing fewer logs.
It is producing none, and the gap is invisible in the logging backend because absence looks
identical to quiet.

The same applies to a security agent: the node is unmonitored, and nothing reports that it is.

## Why a node gets skipped

**Taints without a matching toleration.** The most common cause by far. A tainted GPU or spot
node pool is excluded unless the DaemonSet tolerates it, and new node pools frequently arrive
with taints the existing DaemonSets were never updated for.

**Insufficient allocatable resources.** A fully-packed node has nothing left for the DaemonSet
pod, which requests resources like any other. DaemonSet pods have no special reservation.

**Node affinity or selectors** that exclude nodes deliberately. That is legitimate, and worth confirming
before treating this as a defect.

**Unschedulable nodes**, cordoned during maintenance.

## Read desiredNumberScheduled, not your node count

The status field already accounts for affinity and node selectors, so it reflects the nodes the
DaemonSet is actually meant to cover. Comparing against your total node count will produce false
findings on any DaemonSet that deliberately targets a subset.

## Diffing node names against DaemonSet pods

```bash
kubectl get daemonset -A -o json | jq -r '
  .items[] | select(.status.numberReady < .status.desiredNumberScheduled)
  | "\(.metadata.namespace)/\(.metadata.name)\t\(.status.numberReady)/\(.status.desiredNumberScheduled)"'
```

To find which nodes are missing it:

```bash
kubectl get nodes -o name | sed 's|node/||' | sort > /tmp/all
kubectl get pods -n <ns> -l <selector> -o jsonpath='{.items[*].spec.nodeName}' | tr ' ' '\n' | sort > /tmp/has
comm -23 /tmp/all /tmp/has
```

Then check that node's taints. That is the answer roughly nine times in ten.
