[email protected]
BelgiumFranceSwitzerlandUnited Arab Emirates
LinkedInFacebook
Kube IT Consulting
My coursesContact us

PodDisruptionBudgets: the reason your node drain hangs

What a PDB does and does not protect against, the single-replica trap that blocks upgrades forever, and how to set minAvailable so maintenance can still happen.

What a PDB covers

Protects against
Voluntary disruption (drain, eviction)
Ignores
Node crash, OOM kill, kubelet death
Enforced by
The eviction API
On violation
Eviction is refused, drain retries
Common trap
1 replica + minAvailable 1 = never

kubectl drain node-7 sits there. No error, no timeout, just a periodic line about evicting a pod that never goes away. Somewhere a PodDisruptionBudget is doing exactly what it was told to do.

A PDB constrains one specific thing

It bounds voluntary disruption: the eviction API, which is what kubectl drain, cluster autoscalers and node upgrades use.

It has no effect on involuntary disruption. A node losing power, a kernel panic, an OOM kill, a kubelet that stops reporting — none of these consult a PDB, because none of them go through the eviction API. Your minAvailable: 3 will not keep three pods running when the rack loses power.

That distinction disappoints people who read PDBs as an availability guarantee. They are a politeness contract with the things that drain nodes on purpose.

The trap that blocks upgrades indefinitely

apiVersion: policy/v1
kind: PodDisruptionBudget
metadata:
  name: api
spec:
  minAvailable: 1
  selector:
    matchLabels:
      app: api

Paired with a Deployment running replicas: 1, this says: at least one pod must always be available, and there is exactly one pod. Evicting it would leave zero. So the eviction API refuses, forever, and the drain retries until a human intervenes.

This is not a rare misconfiguration. It is the default shape of a first PDB written by someone protecting a service that has not been scaled yet, and it is the most common cause of an OpenShift or Kubernetes upgrade that stops halfway through a worker pool.

Find them before they find you:

kubectl get pdb -A -o custom-columns=\
NS:.metadata.namespace,NAME:.metadata.name,\
MIN:.spec.minAvailable,MAX:.spec.maxUnavailable,\
ALLOWED:.status.disruptionsAllowed,CURRENT:.status.currentHealthy

Any row with ALLOWED at 0 will block a drain right now. Some of those are correct and temporary — a pod mid-rollout, a deployment that is genuinely degraded. A row that has been at zero for days is a configuration problem.

minAvailable or maxUnavailable

Both exist and you may set only one.

maxUnavailable is usually the better choice, because it expresses the tolerance rather than the target and it keeps meaning the right thing when the replica count changes.

spec:
  maxUnavailable: 1     # one pod may go at a time, whatever the replica count

With minAvailable: 3 and a scale-down to 3 replicas, you have silently created the blocking configuration above. With maxUnavailable: 1, scaling changes nothing about whether maintenance can proceed.

Percentages work for both and round in the direction that favours availability, which is worth remembering when the numbers are small: minAvailable: 50% of 3 replicas is 2, not 1.5.

Sensible defaults by workload shape

Workload Setting Why
Stateless web, 3+ replicas maxUnavailable: 1 Drains proceed one node at a time
Stateless web, large fleet maxUnavailable: 10% Drains parallelise without collapsing capacity
Quorum store (etcd, ZooKeeper) maxUnavailable: 1 Never risk two members at once
Single-replica stateful no PDB A PDB cannot help, and will block maintenance
Batch / Job no PDB Restart is the normal path

The single-replica row is the one worth arguing about internally. A PDB there does not add availability — there is no second replica to keep — it only converts a brief restart into a stalled upgrade. If the workload genuinely cannot tolerate a restart, the fix is a second replica or a scheduled maintenance window, not a budget that refuses eviction.

Clearing a stuck drain safely

When a drain is blocked and you need it to proceed, in order of preference:

Scale up first. If the app can run two replicas, kubectl scale --replicas=2 makes the PDB satisfiable and the drain completes with no downtime. This is almost always available and almost always skipped.

Wait, if the block is transient. disruptionsAllowed: 0 during a rollout resolves itself once the new pods are Ready. Check currentHealthy against desiredHealthy before assuming it is stuck.

Delete the pod, accepting the disruption. kubectl delete pod bypasses the eviction API and the PDB entirely. This is a decision to take the outage, made deliberately, in a window — not a trick to make the warning go away.

Never delete the PDB to unblock a drain. It works, and it removes the protection everywhere it applied, and nobody puts it back.

Where PDBs and drains meet in practice

A drain evicts in no guaranteed order, so a PDB is the only thing stopping two members of a quorum leaving together. Combine it with topologySpreadConstraints so replicas are not all on the node being drained in the first place:

      topologySpreadConstraints:
        - maxSkew: 1
          topologyKey: kubernetes.io/hostname
          whenUnsatisfiable: ScheduleAnyway
          labelSelector:
            matchLabels:
              app: api

Spread constraints reduce how often the PDB has to say no. The PDB is the backstop for when it does.

If you run OpenShift, this is the mechanism behind upgrades that stall on a worker pool, and the PDB query above is the first thing to run when one does.

Next steps

Practise it

Run the CKA track in a real terminal

Every objective on CertLabs is graded against live system state rather than the command you typed, on a sandboxed cluster that resets between exercises. The CKA track covers CNCF, kubeadm, etcd.

Open CertLabs

CertLabs is our own practice platform.

Get help

Running this in production?

We operate Kubernetes and OpenShift for clients across the EU and the Gulf, and train the teams who inherit them. Platform assessments, migrations and hands-on enablement.

Talk to us