# PDB maxUnavailable Is Zero

> Flags PodDisruptionBudgets whose maxUnavailable is 0 or 0%, a spec that forbids every voluntary eviction.

Source: https://zop.dev/integrations/kubernetes/recommendations/pdb-maxunavailable-is-zero

---

## Zero unavailable means zero evictions

`maxUnavailable` sets how many selected pods may be down after an eviction. The
[PodDisruptionBudget API reference](https://kubernetes.io/docs/reference/kubernetes-api/policy-resources/pod-disruption-budget-v1/)
uses 0 as its own example of a value that prevents all voluntary evictions. The
[disruption budget task page](https://kubernetes.io/docs/tasks/run-application/configure-pdb/)
confirms that `0` and `0%` both require zero voluntary evictions, and that a node running one of
those pods cannot be drained: the drain simply never finishes. Kubernetes permits the setting,
but every routine operation that moves pods, from node upgrades to scale-downs, will wait on it.

The value is often written with good intent, to protect a quorum or a stateful primary. The
budget cannot tell a planned upgrade from an accident, though, so it blocks both.

## Listing budgets that forbid eviction

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

## A spec check, independent of pod health

ZopNight reads the `maxUnavailable` value recorded for each budget and fires when it is exactly
`0` or `0%`. Nothing else is weighed: not the pod count, not pod health, not the budget's age. A
budget that uses `minAvailable` instead, or sets no `maxUnavailable`, is outside this check.
Changing the value to anything above zero clears the finding on the next evaluation.

## How it relates to the other budget findings

Because this is a spec check, it catches the problem before any drain fails. The live counterpart,
[PDB blocks all voluntary disruptions](https://zop.dev/integrations/kubernetes/recommendations/pdb-blocks-all-voluntary-disruptions),
reads the status and also fires when a `minAvailable` budget has no room left, so a budget with
`maxUnavailable: 0` and running pods usually appears in both. Budgets whose selector finds no
pods are reported by
[PDB matches no pods](https://zop.dev/integrations/kubernetes/recommendations/pdb-matches-no-pods).

## An operational cost, not a billing one

No saving is calculated. The consequence is maintenance that cannot finish: node pools stuck on
old versions and nodes kept alive only because their pods cannot leave.

## Allowing one pod out at a time

1. Decide how many replicas the application can lose briefly. For most services with three or
   more replicas, one is safe.
2. Set `maxUnavailable: 1`, or a percentage. Kubernetes rounds a `maxUnavailable` percentage up,
   so even `10%` of three pods allows one eviction.
3. Apply the change with `kubectl apply` and confirm `status.disruptionsAllowed` is now at least 1.
4. If the workload truly must never be evicted, document the exception and plan how its nodes
   will be maintained by hand.

**Note**
`maxUnavailable` only works for pods that share one controller, such as a single Deployment or StatefulSet. For arbitrary pod groups, use `minAvailable` instead.
