# PDB Matches No Pods

> Flags PodDisruptionBudgets older than 10 minutes whose status.expectedPods is 0, meaning the selector protects no pods.

Source: https://zop.dev/integrations/kubernetes/recommendations/pdb-matches-no-pods

---

## A disruption budget with nothing to guard

A PodDisruptionBudget limits how many pods of an application voluntary disruptions, such as a
node drain, may take down at once. The disruption controller counts the pods matching the PDB's
selector and publishes the total in `status.expectedPods`, which the
[PodDisruptionBudget API reference](https://kubernetes.io/docs/reference/kubernetes-api/policy-resources/pod-disruption-budget-v1/)
defines as the total number of pods counted by the budget.

When that count is zero, the PDB is dead configuration. The team believes the service is shielded
during cluster upgrades, but the pods it runs today carry different labels from the ones the
selector names, so a drain can evict every replica at once. The
[configure a PDB task](https://kubernetes.io/docs/tasks/run-application/configure-pdb/) shows the
symptom directly: with no matching pods, `kubectl get poddisruptionbudgets` reports zero allowed
disruptions.

## Checking every PDB's pod count

```bash
kubectl get poddisruptionbudgets -A -o json | jq -r '
  .items[]
  | select(.status.expectedPods == 0)
  | "\(.metadata.namespace)/\(.metadata.name) selector=\(.spec.selector | tojson)"'
```

Then compare the selector with the labels the intended pods really carry:

```bash
kubectl get pods -n <namespace> --show-labels
```

## Zero pods, and older than ten minutes

ZopNight reads the expected pod count it collected for the PDB and fires only when the count is
exactly zero. A PDB created moments before its workload has not had time to see the pods, so the
rule also requires the PDB to be at least 10 minutes old, judged from its creation timestamp.
When that timestamp or the pod count is missing, the rule stays silent. If a zero count turns out
to be brief, the next evaluation sees the pods and the finding clears.

## Budgets it does not flag

A PDB that counts even one pod is out of scope here; budgets that count pods but allow no
evictions are handled by
[PDB blocks all voluntary disruptions](https://zop.dev/integrations/kubernetes/recommendations/pdb-blocks-all-voluntary-disruptions)
and [PDB maxUnavailable is zero](https://zop.dev/integrations/kubernetes/recommendations/pdb-maxunavailable-is-zero).
One detail worth knowing when you read the selector: in the `policy/v1` API an empty selector
matches every pod in the namespace, so an empty selector is not a way to get a zero count.

## Governance item with no cost attached

The recommendation has no savings figure. The PDB object is free; the exposure is an outage during
maintenance that the budget was supposed to prevent.

## Repairing or removing the budget

1. Identify the workload the PDB was written for, usually from its name or the history of the
   manifest in source control.
2. If that workload still exists, update `.spec.selector.matchLabels` to match the labels in its
   pod template and apply the change.
3. Confirm with `kubectl get pdb <name> -n <namespace>` that the allowed disruptions and expected
   pod count now reflect the replicas.
4. If the workload is gone, delete the PDB with `kubectl delete pdb <name> -n <namespace>`.
