# PDB Blocks All Voluntary Disruptions

> Flags PodDisruptionBudgets that allow no evictions while they cover running pods, which stalls node drains and upgrades.

Source: https://zop.dev/integrations/kubernetes/recommendations/pdb-blocks-all-voluntary-disruptions

---

## A budget with no room freezes node maintenance

Draining a node goes through the Eviction API, and the
[API-initiated eviction page](https://kubernetes.io/docs/concepts/scheduling-eviction/api-eviction/)
spells out the response when a PodDisruptionBudget has no room left: `429 Too Many Requests`, the
eviction is not currently allowed and may be attempted again later. The
[disruption budget task page](https://kubernetes.io/docs/tasks/run-application/configure-pdb/)
is blunt about the outcome: if you try to drain a node where an unevictable pod is running, the
drain never completes. Node upgrades, repairs and scale-downs all queue behind it.

A budget reaches zero allowed disruptions by two routes. The spec may demand every pod, for
example `minAvailable` equal to the replica count or `100%`. Or the spec is reasonable but some
pods are unhealthy, so the number of healthy pods is already at or below `status.desiredHealthy`.
Either way, the disruption controller sets the budget's `DisruptionAllowed` condition to False with reason `InsufficientPods`, meaning the pod count is at or below what the budget requires.

## Reading the allowance yourself

```bash
kubectl get poddisruptionbudgets -A -o json | jq -r '
  .items[]
  | select(.status.disruptionsAllowed == 0 and .status.expectedPods > 0)
  | "\(.metadata.namespace)/\(.metadata.name) healthy=\(.status.currentHealthy) desired=\(.status.desiredHealthy) expected=\(.status.expectedPods)"'
```

When `currentHealthy` is below `expectedPods`, look at the pods before the budget.

## Two status numbers, compared as they stand

ZopNight reads the allowed-disruption count and the expected pod count from the budget's status as
last collected. It fires when the allowance is exactly 0 and the budget counts at least one pod.
If either number is missing, it stays silent. Because it reads the live allowance rather than the
spec, the result reflects both over-strict budgets and budgets drained of headroom by failing
pods, and it clears as soon as an evaluation sees room for one eviction.

## Budgets handled elsewhere

A budget that counts no pods at all is reported by
[PDB matches no pods](https://zop.dev/integrations/kubernetes/recommendations/pdb-matches-no-pods). A spec with
`maxUnavailable` of 0 is also reported by
[PDB maxUnavailable is zero](https://zop.dev/integrations/kubernetes/recommendations/pdb-maxunavailable-is-zero),
so one budget can carry both findings. Keep in mind that node-pressure evictions by the kubelet
do not respect budgets at all; a PDB only governs voluntary disruptions.

## Stalled upgrades instead of a dollar figure

The finding is a high-severity reliability item with no savings attached. The cost shows up as
maintenance windows that overrun, nodes that cannot be retired, and security patches that wait.

## Giving the budget room to move

1. Run `kubectl describe poddisruptionbudget <name> -n <namespace>` and compare healthy pods with
   the desired count.
2. If pods are unhealthy, fix them first; the budget will open up once they report Ready.
3. If the spec itself demands every pod, lower `minAvailable` below the replica count or switch
   to `maxUnavailable: 1`. A budget may set only one of the two fields.
4. For workloads with a single replica, add a second one before relying on a budget, as
   [Single replica deployment](https://zop.dev/integrations/kubernetes/recommendations/single-replica-deployment)
   explains.
5. Consider `unhealthyPodEvictionPolicy: AlwaysAllow`, which lets running pods that are not yet
   healthy be evicted regardless of the budget, so a crash-looping pod cannot hold a drain hostage.
