A PodDisruptionBudget caps how many pods of one application can be down at once during a voluntary disruption, such as the node drains that happen on every Kubernetes upgrade and repair. Without one, a drain can evict your last pod and take the application offline. With one, the drain waits. Get one wrong and it fails in two directions, both only visible mid-maintenance: a budget with no slack, or one too tight to satisfy. ## What a PDB does and does not protect A PDB protects against **voluntary** disruptions: node drains, and anything else that asks Kubernetes to move a pod politely. It does **not** protect against a node crash or hardware failure, because nothing asks permission there. So a PDB is no substitute for running enough replicas: replicas are the floor, and the PDB just meters how fast a drain works through them. ## minAvailable or maxUnavailable Pick exactly one: ```yaml apiVersion: policy/v1 kind: PodDisruptionBudget metadata: name: web spec: maxUnavailable: 1 # or: minAvailable: 2 selector: matchLabels: app: web ``` `maxUnavailable: 1` lets a drain take at most one pod at a time, the right default for a horizontally scaled service. `minAvailable: 2` keeps a fixed number serving no matter what. Either way, match the number to your replica count: three replicas with `maxUnavailable: 1` leaves two up throughout a rolling drain. And put the pods on different nodes, or the budget has nothing to spread. See [topology spread](/docs/hetzner/apalla/workloads/placement/topology-spread-constraints). ## Why ALLOWED DISRUPTIONS reads 0 ```console $ kubectl get pdb -n team-web web NAME MIN AVAILABLE MAX UNAVAILABLE ALLOWED DISRUPTIONS AGE web N/A 1 1 2m ``` `ALLOWED DISRUPTIONS` is how many pods may be evicted right now. If it reads `0` when you expect `1`, a pod is currently not ready, so the budget has no slack to give. A drain then waits for that pod to recover. A `0` that never climbs means a pod is stuck. Fix its readiness before the next maintenance, not during it. ## When a drain hangs and will not finish The other symptom looks like a hang. The drain grinds through the full 180 seconds, then evicts the pod anyway, because the budget was set too tight to ever satisfy. A drain waits only as long as `nodeDrainTimeoutSeconds` allows, a per-pool default of 180 seconds that is [raisable per pool](/docs/hetzner/apalla/workloads/production/run-a-production-ready-workload) in the `Cluster` topology. After that it proceeds, and the pods on that node go with it. A PDB provides an orderly, one-at-a-time move, never an indefinite block. > [!WARNING] > A single-replica application with `maxUnavailable: 0` (or `minAvailable: 1`) tells the drain it may never evict the pod. The drain waits out the whole timeout and takes the pod regardless. A PDB cannot make one replica highly available. Run two. ## Stateful and quorum applications For a quorum-based application such as etcd or a database with a raft group, size the PDB so a drain never breaks quorum: with three members, `maxUnavailable: 1`. Pair it with one-per-node placement so no single drain can reach two members at once. See [Keep stateful pods safe across node replacement](/docs/hetzner/apalla/workloads/stateful/stateful-placement-and-node-replacement).