PodDisruptionBudgets whose selector matches zero pods
What does ZopNight detect here?
ZopNight flags a Kubernetes PodDisruptionBudget on EKS, GKE or AKS when its `status.expectedPods` is 0 and the PDB is more than 10 minutes old. A budget that counts no pods protects nothing during node drains or upgrades, and usually means its label selector outlived a renamed or deleted workload.
Signal and threshold
| Field | Value |
|---|---|
| Rule IDs | RC-1749 · RC-1849 · RC-1949 |
| Category | governance |
| Severity | low |
| Metric | status.expectedPods |
| Threshold | equals 0, PDB older than 10 minutes |
| Evaluation window | 10m |
| Source | ZopNight |
| Permissions used | list poddisruptionbudgets.policy |
Where it applies
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
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 shows the
symptom directly: with no matching pods, kubectl get poddisruptionbudgets reports zero allowed
disruptions.
Checking every PDB’s pod count
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:
kubectl get pods -n <namespace> --show-labelsZero 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
and 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
- Identify the workload the PDB was written for, usually from its name or the history of the manifest in source control.
- If that workload still exists, update
.spec.selector.matchLabelsto match the labels in its pod template and apply the change. - Confirm with
kubectl get pdb <name> -n <namespace>that the allowed disruptions and expected pod count now reflect the replicas. - If the workload is gone, delete the PDB with
kubectl delete pdb <name> -n <namespace>.