Home DevOps Kubernetes PodDisruptionBudget: Stop Losing Pods During Rolling Updates
Advanced 5 min · July 18, 2026
Kubernetes Pod Disruption Budgets: High Availability

Kubernetes PodDisruptionBudget: Stop Losing Pods During Rolling Updates

Kubernetes PodDisruptionBudget prevents voluntary disruptions from killing your app.

N
Naren Founder & Principal Engineer

20+ years shipping production infrastructure and CI/CD at scale. Lessons pulled from things that broke in production.

Follow
Production
production tested
July 18, 2026
last updated
2,466
articles · all by Naren
Before you start⏱ 20 min
  • Kubernetes cluster with multiple nodes
  • Understanding of Deployments and ReplicaSets
  • kubectl configured
 ● Production Incident 🔎 Debug Guide ⚙ Triage Commands
Quick Answer

Use kubectl create pdb --selector= to protect your app. For a 3-replica deployment, set minAvailable: 2 or maxUnavailable: 1. This ensures at least 2 pods stay running during voluntary disruptions.

✦ Definition~90s read
What is Kubernetes Pod Disruption Budgets?

A PodDisruptionBudget (PDB) is a Kubernetes resource that limits the number of pods of a replicated application that can be down simultaneously from voluntary disruptions like node drains or cluster upgrades.

Imagine you're running a food truck with three cooks.
Plain-English First

Imagine you're running a food truck with three cooks. A PDB is like a rule that says 'never let more than one cook leave at a time.' If you need to send one for a break (voluntary disruption), the others keep serving. But if a cook collapses (involuntary disruption), the rule doesn't apply — you just deal with it.

You've seen it: a rolling update or node drain takes down all your pods at once, and your service goes dark. Kubernetes gives you the tools to prevent this, but most teams ignore them until it's too late. PodDisruptionBudgets are the difference between a graceful maintenance and a PagerDuty alert at 3 AM.

The problem is simple: voluntary disruptions — node drains, cluster upgrades, autoscaling down — can evict pods. Without a PDB, the eviction controller doesn't care about your app's availability. It'll drain a node hosting all your replicas, and boom, downtime.

By the end of this, you'll know exactly how to configure PDBs for zero-downtime maintenance, how to debug them when they block an operation, and the one gotcha that makes people rip their hair out.

What Exactly Is a Voluntary Disruption?

Kubernetes splits disruptions into two types: voluntary and involuntary. Involuntary disruptions are hardware failures, kernel panics, OOM kills — things you can't control. Voluntary disruptions are actions you or the cluster take: node drains, cluster autoscaler scale-down, rolling updates, pod evictions via API. A PDB only protects against voluntary disruptions. If a node dies, your pods are gone — PDB won't save you. But for everything else, it's your safety net.

Without a PDB, the eviction controller treats each pod independently. It doesn't know your app needs at least 2 replicas. So when you drain a node, it evicts all pods on that node, even if that means your app goes to zero. That's the bug.

PDBs work by intercepting eviction requests. When you drain a node or delete a pod, the eviction API checks the PDB. If evicting the pod would drop the available count below minAvailable (or exceed maxUnavailable), the eviction is rejected. The drain pauses, and the controller waits until a new pod is ready elsewhere.

pdb-basic.yamlYAML
1
2
3
4
5
6
7
8
9
10
11
// io.thecodeforge — DevOps tutorial

apiVersion: policy/v1
kind: PodDisruptionBudget
metadata:
  name: payments-api-pdb
spec:
  minAvailable: 2  # At least 2 pods must always be running
  selector:
    matchLabels:
      app: payments-api
Output
Created poddisruptionbudget.policy/payments-api-pdb
💡Senior Shortcut:
Use maxUnavailable instead of minAvailable when you want to express 'I can tolerate losing this many pods.' It's more intuitive for rolling updates: maxUnavailable: 1 means 'never take down more than 1 pod at a time.'

minAvailable vs maxUnavailable: When to Use Which

You have two knobs: minAvailable and maxUnavailable. They're mutually exclusive — pick one. minAvailable specifies the absolute number or percentage of pods that must remain available. maxUnavailable specifies how many can be unavailable. For a 3-replica deployment, minAvailable: 2 is equivalent to maxUnavailable: 1.

Use minAvailable when you have a hard floor on capacity. For example, a database read replica pool must always have at least 3 replicas to handle query load. Use maxUnavailable when you know your burst tolerance. For a web app that can handle a 50% traffic drop briefly, maxUnavailable: 50% is fine.

The percentage is calculated from the current number of replicas, not the desired. If you scale down from 10 to 5, maxUnavailable: 50% allows 5 pods down at the start, but as pods are removed, the percentage shrinks. This can cause unexpected blocking. I've seen a scale-down get stuck because maxUnavailable: 50% of 5 is 2.5 (rounded up to 3), but after evicting 3, only 2 remain, and the next eviction would violate the budget. The fix: use absolute numbers for critical apps.

pdb-maxunavailable.yamlYAML
1
2
3
4
5
6
7
8
9
10
11
// io.thecodeforge — DevOps tutorial

apiVersion: policy/v1
kind: PodDisruptionBudget
metadata:
  name: web-frontend-pdb
spec:
  maxUnavailable: 1  # Never more than 1 pod down at a time
  selector:
    matchLabels:
      app: web-frontend
Output
Created poddisruptionbudget.policy/web-frontend-pdb
⚠ Production Trap:
Percentage-based PDBs can block scale-downs. If you have 3 pods and maxUnavailable: 50%, that's 1.5 → 2 pods allowed down. After evicting 2, only 1 remains, and the next eviction is blocked. The scale-down hangs. Use absolute numbers for critical paths.

How PDBs Interact with Rolling Updates and Node Drains

A common misconception: the Deployment's maxSurge and maxUnavailable in the update strategy control all disruptions. They don't. Those only apply during a rolling update triggered by a Deployment change. Node drains, cluster autoscaler scale-down, and manual evictions bypass the Deployment's strategy entirely. That's where PDBs come in.

When you kubectl drain a node, the eviction API checks the PDB for each pod on that node. If evicting a pod would violate the PDB, the drain pauses. The node gets marked as SchedulingDisabled, and the drain command waits. It won't time out — it'll sit there until the PDB allows the eviction. This is often what people see when a drain hangs.

Rolling updates also respect PDBs. The Deployment controller creates new pods, waits for them to be ready, then evicts old pods. But the eviction of old pods goes through the eviction API, which checks the PDB. If the PDB says maxUnavailable: 1, the rolling update will only evict one old pod at a time, even if the Deployment strategy allows more. This is usually fine, but it can slow down updates. If you need speed, increase maxUnavailable temporarily.

drain-example.shBASH
1
2
3
4
5
6
7
8
9
10
11
12
// io.thecodeforge — DevOps tutorial

# Drain a node with a PDB protecting the pods
kubectl drain node-1 --ignore-daemonsets
# Output will show eviction attempts and possibly wait
# If PDB blocks, you'll see:
#   evicting pod default/payments-api-xxx
#   error when evicting pods: Cannot evict pod as it would violate the pod's disruption budget.
# The drain will retry indefinitely until a new pod is ready.

# To force drain (skip PDB check) — use with caution:
kubectl drain node-1 --ignore-daemonsets --disable-eviction
Output
node/node-1 cordoned
pod/payments-api-xxx evicted
pod/payments-api-yyy evicted
...
⚠ Never Do This:
--disable-eviction bypasses PDBs entirely. It force-deletes pods. Only use this when a node is already failing and you need to evacuate immediately. Otherwise, you're asking for downtime.

PDB and Cluster Autoscaler: The Silent Scale-Down Blocker

Cluster Autoscaler (CA) scales down nodes when they're underutilized. It evicts pods from those nodes before removing them. If a PDB blocks the eviction, CA can't scale down that node. This leads to nodes sitting idle, wasting money.

I've seen this in production: a team had a PDB with minAvailable: 3 on a 3-replica deployment. CA tried to scale down a node that hosted one of those replicas. The eviction was blocked because removing that pod would leave only 2 available, violating the PDB. CA gave up, and the node stayed. The fix: either set minAvailable: 2 or use maxUnavailable: 1 to allow one pod to be evicted.

Another gotcha: CA uses a different eviction mechanism than kubectl drain. It respects PDBs the same way, but it has a timeout. If the eviction is blocked for too long (default 10 minutes), CA moves on. This can leave pods stranded on a node that should have been scaled down. Monitor CA logs for skip node ... - pod ... has a disruption budget that prevents eviction.

pdb-ca-friendly.yamlYAML
1
2
3
4
5
6
7
8
9
10
11
// io.thecodeforge — DevOps tutorial

apiVersion: policy/v1
kind: PodDisruptionBudget
metadata:
  name: worker-pdb
spec:
  maxUnavailable: 1  # Allow CA to evict one pod at a time
  selector:
    matchLabels:
      app: worker
Output
Created poddisruptionbudget.policy/worker-pdb
🔥Interview Gold:
Question: 'How does Cluster Autoscaler handle PDBs?' Answer: CA respects PDBs but has a 10-minute timeout. If eviction is blocked, CA logs a message and skips the node. This can cause scale-down failures and wasted nodes.

Unhealthy Pod Eviction: The PDB Bypass You Need to Know

There's a critical exception: PDBs do not protect unhealthy pods. If a pod has a status condition like Ready=False or has been in Terminating for more than a few minutes, the eviction controller may evict it even if it would violate the PDB. This is by design — you don't want a broken pod blocking a drain.

But here's the trap: the definition of 'unhealthy' is strict. The pod must have a status.conditions entry with type: Ready and status: False. If the pod is simply not ready (e.g., liveness probe failing but condition not updated), it's still considered healthy by the eviction controller. I've seen a pod with a failing liveness probe block a drain for 5 minutes because the condition wasn't set to False.

To force eviction of a stuck pod, you can delete it directly with kubectl delete pod --force --grace-period=0. This bypasses the eviction API entirely. Use this when a pod is truly dead and blocking operations.

force-delete.shBASH
1
2
3
4
5
// io.thecodeforge — DevOps tutorial

# Force delete a pod that's blocking a drain
kubectl delete pod payments-api-xxx --force --grace-period=0
# This bypasses the eviction API and PDB check
Output
pod "payments-api-xxx" force deleted
⚠ Production Trap:
Force-deleting a pod doesn't respect PDBs. Only do this when the pod is unhealthy and the drain is stuck. Otherwise, you risk downtime.

Debugging PDBs: Why Is My Drain Stuck?

When a drain hangs, the first thing to check is the PDB status. Run kubectl get pdb -n <namespace>. Look at the DISRUPTIONS ALLOWED column. If it's 0, the PDB is blocking evictions. Then check EXPECTED PODS and CURRENT HEALTHY — if they're equal, no pod can be evicted without violating the budget.

Next, check which pods are on the draining node: kubectl get pods --field-selector spec.nodeName=<node>. See if any of those pods match the PDB selector. If they do, you need to either increase maxUnavailable, scale up the deployment, or force the drain.

Another common issue: the PDB selector doesn't match any pods. This happens when labels change. The PDB will show EXPECTED PODS: 0, meaning it's a no-op. You think you're protected, but you're not. Always verify with kubectl describe pdb <name>.

debug-pdb.shBASH
1
2
3
4
5
6
7
8
9
10
11
12
13
14
// io.thecodeforge — DevOps tutorial

# Check PDB status
kubectl get pdb -n default
# Example output:
# NAME              MIN AVAILABLE   MAX UNAVAILABLE   ALLOWED DISRUPTIONS   AGE
# payments-api-pdb   2               N/A               1                     10m

# Describe PDB for details
kubectl describe pdb payments-api-pdb
# Shows selector, status, and which pods match

# List pods on a specific node
kubectl get pods --field-selector spec.nodeName=node-1 -o wide
Output
NAME MIN AVAILABLE MAX UNAVAILABLE ALLOWED DISRUPTIONS AGE
payments-api-pdb 2 N/A 1 10m
...
💡Senior Shortcut:
When a drain is stuck, check kubectl get events --field-selector involvedObject.kind=PodDisruptionBudget. It shows eviction attempts and why they were rejected.

When Not to Use a PDB

PDBs aren't free. They add latency to drains and rolling updates. For stateless batch jobs that can be interrupted, skip the PDB. For development clusters, skip it — you want fast iteration, not safety nets. For single-replica deployments, a PDB with minAvailable: 1 is pointless because evicting that one pod would always violate it, blocking all drains. Either run multiple replicas or accept downtime during maintenance.

Also, don't use PDBs for DaemonSets. DaemonSet pods are tied to nodes — draining a node will evict the DaemonSet pod regardless. PDBs don't apply to DaemonSets in practice because the eviction controller treats them specially (they're allowed to be evicted even with a PDB). Check the docs, but I've seen this confuse people.

🔥Never Do This:
Don't set minAvailable: 1 on a single-replica deployment. It blocks all drains and updates. Either run at least 2 replicas or accept the risk.
● Production incidentPOST-MORTEMseverity: high

The Node Drain That Killed Our API

Symptom
During a routine node drain, our payments API returned 503 for 2 minutes. The team saw all 3 pods go down simultaneously.
Assumption
We assumed the Deployment's maxSurge: 1 and maxUnavailable: 0 would protect us.
Root cause
No PDB existed. The eviction controller didn't check the Deployment's update strategy — it just evicted all pods on the node. Since all 3 replicas were on that node, they all went down.
Fix
Applied a PDB with minAvailable: 2 and re-drained. The drain paused until a new pod was ready elsewhere.
Key lesson
  • Deployment update strategy only controls rolling updates, not node drains or cluster upgrades.
  • Always pair with a PDB.
Production debug guideSystematic recovery paths for the failure modes engineers actually hit.3 entries
Symptom · 01
kubectl drain hangs with 'Cannot evict pod as it would violate the pod's disruption budget'
Fix
1. Run kubectl get pdb -n <ns> to see ALLOWED DISRUPTIONS. If 0, the budget is full. 2. Check kubectl describe pdb <name> for CURRENT HEALTHY vs EXPECTED PODS. 3. Either scale up the deployment (increase replicas) to create slack, or increase maxUnavailable temporarily. 4. If urgent, use kubectl drain --disable-eviction but be aware of downtime risk.
Symptom · 02
Cluster Autoscaler not scaling down nodes
Fix
1. Check CA logs: kubectl logs -n kube-system cluster-autoscaler-xxx. Look for 'disruption budget prevents eviction'. 2. Identify the PDB blocking: kubectl get pdb --all-namespaces. 3. Adjust PDB to allow at least one pod to be evicted (e.g., set maxUnavailable: 1). 4. Consider using --max-graceful-termination-sec to speed up evictions.
Symptom · 03
Rolling update takes longer than expected
Fix
1. Check PDB status during update: kubectl get pdb -w. 2. If ALLOWED DISRUPTIONS is 0, the update is waiting for a new pod to become ready. 3. Increase maxUnavailable temporarily to allow faster rollouts. 4. Alternatively, increase maxSurge in the Deployment to create more new pods before evicting old ones.
★ Kubernetes PodDisruptionBudget Triage Cheat SheetFirst-response commands for when things go wrong — copy-paste ready.
Drain hangs: `Cannot evict pod as it would violate the pod's disruption budget`
Immediate action
Check PDB status
Commands
kubectl get pdb --all-namespaces
kubectl describe pdb <name> -n <ns>
Fix now
kubectl drain <node> --disable-eviction (force) OR scale up deployment to create slack
Cluster Autoscaler not scaling down: `skip node ... pod ... has a disruption budget that prevents eviction`+
Immediate action
Check CA logs
Commands
kubectl logs -n kube-system cluster-autoscaler-<pod> | grep 'disruption budget'
kubectl get pdb --all-namespaces
Fix now
Edit PDB to increase maxUnavailable or decrease minAvailable
Rolling update slow: pods not being replaced+
Immediate action
Check PDB during update
Commands
kubectl get pdb -w
kubectl rollout status deployment/<name>
Fix now
Temporarily increase maxUnavailable: kubectl edit pdb <name>
PDB shows `EXPECTED PODS: 0`+
Immediate action
Check selector matches
Commands
kubectl describe pdb <name>
kubectl get pods -l <selector>
Fix now
Update PDB selector to match actual pod labels
FeatureminAvailablemaxUnavailable
ExpressionAbsolute or percentageAbsolute or percentage
Use caseHard capacity floorBurst tolerance
Example (3 replicas)minAvailable: 2maxUnavailable: 1
Scale-down behaviorCan block if too highCan block if percentage rounds up
⚙ Quick Reference
6 commands from this guide
FileCommand / CodePurpose
pdb-basic.yamlapiVersion: policy/v1What Exactly Is a Voluntary Disruption?
pdb-maxunavailable.yamlapiVersion: policy/v1minAvailable vs maxUnavailable
drain-example.shkubectl drain node-1 --ignore-daemonsetsHow PDBs Interact with Rolling Updates and Node Drains
pdb-ca-friendly.yamlapiVersion: policy/v1PDB and Cluster Autoscaler
force-delete.shkubectl delete pod payments-api-xxx --force --grace-period=0Unhealthy Pod Eviction
debug-pdb.shkubectl get pdb -n defaultDebugging PDBs

Key takeaways

1
PDBs only protect against voluntary disruptions
node drains, cluster upgrades, rolling updates. Involuntary failures (node death) bypass them.
2
Use maxUnavailable for burst tolerance, minAvailable for hard capacity floors. Prefer absolute numbers over percentages for small deployments.
3
PDBs can block Cluster Autoscaler scale-downs. Monitor CA logs for 'disruption budget prevents eviction' and adjust PDBs accordingly.
4
Unhealthy pods (with Ready=False condition) bypass PDBs. Force-delete stuck pods with --force only as a last resort.
INTERVIEW PREP · PRACTICE MODE

Interview Questions on This Topic

Q01SENIOR
How does a PodDisruptionBudget interact with a rolling update when the D...
Q02SENIOR
When would you choose `minAvailable` over `maxUnavailable` in a producti...
Q03SENIOR
What happens when a PDB blocks a pod eviction during a node drain, and t...
Q04JUNIOR
What is a PodDisruptionBudget and why is it important?
Q05SENIOR
You have a 3-replica deployment with `maxUnavailable: 1`. During a node ...
Q06SENIOR
How would you design PDBs for a multi-tenant cluster with varying availa...
Q01 of 06SENIOR

How does a PodDisruptionBudget interact with a rolling update when the Deployment's `maxUnavailable` is set to 0?

ANSWER
The rolling update will create new pods first (via maxSurge), then evict old pods. The eviction of old pods goes through the eviction API, which checks the PDB. If the PDB has maxUnavailable: 1, only one old pod is evicted at a time, even though the Deployment strategy allows zero unavailable. The update proceeds but slower.
FAQ · 4 QUESTIONS

Frequently Asked Questions

01
What is a PodDisruptionBudget in Kubernetes?
02
What's the difference between minAvailable and maxUnavailable?
03
How do I create a PodDisruptionBudget?
04
Can a PodDisruptionBudget block a node drain indefinitely?
N
Naren Founder & Principal Engineer

20+ years shipping production infrastructure and CI/CD at scale. Lessons pulled from things that broke in production.

Follow
Verified
production tested
July 18, 2026
last updated
2,466
articles · all by Naren
🔥

That's Kubernetes. Mark it forged?

5 min read · try the examples if you haven't

Previous
Kubernetes Persistent Volume Claims: Dynamic Provisioning
35 / 43 · Kubernetes
Next
Kustomize: Declarative Configuration Management