Skip to content

Pod Disruption and Cluster Autoscaling

A PodDisruptionBudget limits how many replicas of a workload can be evicted at once during a voluntary disruption — it has no power at all over an involuntary one.

Not every Pod loss is the same kind of event, and the distinction matters for what you can actually plan around.

  • Voluntary disruptions are initiated deliberately by an operator or by automation, and can therefore be controlled, delayed, or rate-limited: a kubectl drain for a node upgrade, kubectl cordon followed by a rolling replacement, or Cluster Autoscaler removing an underutilized Node.
  • Involuntary disruptions are things nobody scheduled: a hardware failure, a kernel panic, a Node simply disappearing. There is no budget or setting that can prevent these — by the time they happen, the Node is already gone.

A PodDisruptionBudget only ever applies to the first category.

PodDisruptionBudget: a floor under voluntary evictions

Section titled “PodDisruptionBudget: a floor under voluntary evictions”

A PodDisruptionBudget (policy/v1) declares either minAvailable or maxUnavailable for a set of Pods selected by label, and any voluntary eviction path — kubectl drain, the Eviction API, Cluster Autoscaler scaling down a Node — has to respect it.

apiVersion: policy/v1
kind: PodDisruptionBudget
metadata:
name: web-app-pdb
spec:
minAvailable: 2
selector:
matchLabels:
app: web-app
Terminal window
# kubectl drain blocks an eviction that would push availability below the PDB
kubectl drain node-3 --ignore-daemonsets --delete-emptydir-data
kubectl get pdb web-app-pdb

With minAvailable: 2 on a Deployment running 3 replicas of web-app, a drain can only evict one Pod at a time — it waits for a replacement to become Ready on another Node before it is allowed to evict the next one. If a hardware failure takes out a Node that had two of those three replicas at once, the PDB does nothing to stop it; it was never built to. It protects against operations choosing to remove too much at once, not against the world doing it to you.

Cluster Autoscaler: adding and removing whole Nodes

Section titled “Cluster Autoscaler: adding and removing whole Nodes”

Where HPA and VPA operate on Pods, Cluster Autoscaler operates one level up, on Nodes:

  • It adds a Node when Pods are stuck Pending because no existing Node has enough free capacity to schedule them.
  • It removes a Node when that Node is significantly underutilized and every Pod on it could be rescheduled elsewhere — but it does this by draining the Node first, respecting any PodDisruptionBudgets along the way, exactly like a manual kubectl drain would.
Terminal window
# Pods stuck here because nothing fits them is the classic trigger for Cluster Autoscaler to add a Node
kubectl get pods --field-selector=status.phase=Pending

A newer alternative worth knowing about is Karpenter, which skips the fixed node-group-template model entirely and provisions right-sized Nodes directly in response to unschedulable Pods, rather than scaling predefined groups up and down.

flowchart LR
  drain["kubectl drain /\nCluster Autoscaler scale-down"] --> pdb{"PodDisruptionBudget\nminAvailable satisfied?"}
  pdb -->|yes| evict["Evict one Pod at a time"]
  pdb -->|no, would violate| wait["Wait before evicting more"]
  evict --> empty["Node fully drained"]
  empty --> remove["Node removed"]
A drain or Cluster Autoscaler scale-down respecting a PodDisruptionBudget before a Node is removed
What is the core difference between a voluntary and an involuntary disruption?
What does a PodDisruptionBudget minAvailable setting actually protect against?
What typically triggers Cluster Autoscaler to add a new Node?
How does Karpenter differ from the traditional Cluster Autoscaler approach?