Kubernetes will sometimes need to evict your Pods — during node drains, cluster upgrades, or autoscaler scale-downs. A Pod Disruption Budget (PDB) tells Kubernetes how much disruption your application can tolerate, preventing it from taking down too many Pods at once.
1. Voluntary vs Involuntary Disruptions
| Type | Cause | PDB Respected? |
|---|---|---|
| Voluntary | kubectl drain, cluster autoscaler scale-down, rolling updates, Pod eviction API | ✅ Yes — PDB blocks the operation until safe |
| Involuntary | Node crash, OOMKill, kernel panic, hardware failure | ❌ No — these happen without asking |
The Eviction API
Voluntary disruptions go through the Eviction API (POST /api/v1/namespaces/{ns}/pods/{name}/eviction). This API checks PDBs before allowing the eviction. Tools that use it:
kubectl drain— evicts all Pods from a node- Cluster Autoscaler — evicts Pods before removing a node
- Descheduler — moves Pods for better balance
- Node upgrades (kubeadm) — cordons and drains nodes
kubectl delete pod does NOT go through the Eviction API — it directly deletes the Pod and ignores PDBs.
kubectl delete pod to "drain" a node manually, PDBs won't protect you. Always use kubectl drain or the Eviction API for controlled operations.
2. PDB Configuration
A PDB targets Pods via a label selector (just like a Service) and specifies one of two constraints:
Option A: minAvailable
apiVersion: policy/v1
kind: PodDisruptionBudget
metadata:
name: web-pdb
spec:
minAvailable: 2 # At least 2 Pods must stay running
selector:
matchLabels:
app: web
# If you have 3 Pods, only 1 can be disrupted at a time (3 - 2 = 1)
Option B: maxUnavailable
apiVersion: policy/v1
kind: PodDisruptionBudget
metadata:
name: web-pdb
spec:
maxUnavailable: 1 # At most 1 Pod can be down at a time
selector:
matchLabels:
app: web
# If you have 3 Pods, 1 can be disrupted (same result as minAvailable: 2)
Percentage Values
spec: minAvailable: "80%" # At least 80% of matched Pods must stay up # 10 Pods × 80% = 8 must stay → 2 can be disrupted spec: maxUnavailable: "25%" # At most 25% can be down # 10 Pods × 25% = 2 can be disrupted at once (rounds up)
minAvailable vs maxUnavailable
| minAvailable | maxUnavailable | |
|---|---|---|
| Semantics | "Keep at least N up" | "Allow at most N down" |
| As replicas scale | Fixed floor — more Pods can be disrupted as you scale up | Fixed disruption rate — scales proportionally |
| Best for | Quorum systems (need minimum N for correctness) | General services (tolerate proportional loss) |
minAvailable OR maxUnavailable, never both. They're two ways to express the same constraint. Choose based on which is more natural for your application's availability requirement.
3. How PDBs Work in Practice
kubectl drain Interaction
Drain Behavior with PDB
# Normal drain (respects PDBs): kubectl drain node-2 --ignore-daemonsets --delete-emptydir-data # Waits if PDB would be violated, retries until budget allows # Drain with timeout: kubectl drain node-2 --timeout=300s # Gives up after 5 minutes if PDB blocks # Force drain (IGNORES PDBs — dangerous!): kubectl drain node-2 --disable-eviction # Directly deletes Pods, bypasses Eviction API entirely
PDB Status
kubectl get pdb # NAME MIN AVAILABLE MAX UNAVAILABLE ALLOWED DISRUPTIONS AGE # web-pdb 2 N/A 1 5d kubectl describe pdb web-pdb # Status: # Current Healthy: 3 # Desired Healthy: 2 # Disruptions Allowed: 1 ← how many can be evicted right now # Expected Pods: 3 # Observed Generation: 1
Disruptions Allowed is the key field. It shows how many Pods can be evicted RIGHT NOW without violating the budget. If it's 0, no voluntary evictions will succeed until a Pod recovers or a new one becomes Ready.
4. Common Patterns & Gotchas
Pattern: Quorum-Based Systems
# etcd (3 nodes, needs quorum of 2):
spec:
minAvailable: 2
selector:
matchLabels:
app: etcd
# Only 1 etcd Pod can be disrupted at a time
# Guarantees quorum is always maintained
Pattern: Stateless Web Service
# Web app with 10 replicas — can lose 20%:
spec:
maxUnavailable: "20%"
selector:
matchLabels:
app: web
# 2 Pods can be evicted simultaneously
# Speeds up node drains while maintaining capacity
Anti-Pattern: minAvailable = replicas
# ⚠️ DANGEROUS: spec: minAvailable: 3 # with exactly 3 replicas # Disruptions Allowed = 0 → drain NEVER succeeds # This blocks cluster upgrades and autoscaler!
minAvailable equal to your replica count (or maxUnavailable: 0). This creates an undrainable workload — cluster operations will hang forever waiting for permission that never comes. Always allow at least 1 disruption.
Gotcha: PDB and Single-Replica Deployments
# Single replica + PDB with minAvailable: 1
spec:
minAvailable: 1
selector:
matchLabels:
app: singleton
# Disruptions Allowed = 0 (can't go below 1 if you only have 1)
# Node drains will block!
# Solution: either accept downtime or run ≥2 replicas
Gotcha: PDB Doesn't Protect During Rolling Updates
Deployment rolling updates use their own maxUnavailable parameter, NOT the PDB. The PDB only applies to external disruptions (drain, autoscaler). The Deployment controller manages its own rollout budget independently.
policy/v1 apiVersion, (2) selector must match the Pods you want to protect, (3) specify exactly one of minAvailable or maxUnavailable.
5. Unhealthy Pod Eviction Policy (K8s 1.27+)
By default, unhealthy Pods (not Ready) still count against the PDB — they can't be evicted either. The new unhealthyPodEvictionPolicy field changes this:
| Policy | Behavior |
|---|---|
IfHealthy (default) | Only evict Pods if the budget allows (unhealthy Pods block drains too) |
AlwaysAllow | Unhealthy Pods can always be evicted regardless of budget |
spec:
maxUnavailable: 1
unhealthyPodEvictionPolicy: AlwaysAllow
selector:
matchLabels:
app: web
# If a Pod is stuck in CrashLoopBackOff, it can be evicted
# without counting against the budget
AlwaysAllow solves the "stuck drain" problem: If a Pod is already unhealthy (CrashLoopBackOff, not Ready), it's not contributing to availability anyway. Letting it be evicted unblocks node operations without sacrificing actual availability.
Summary
| Concept | Key Point |
|---|---|
| PDB | Limits how many Pods can be voluntarily disrupted at once |
| Voluntary disruption | drain, autoscaler, eviction API — respects PDB |
| Involuntary disruption | Node crash, OOMKill — ignores PDB (can't be prevented) |
| minAvailable | "Keep at least N up" — good for quorum systems |
| maxUnavailable | "Allow at most N down" — good for proportional tolerance |
| DisruptionsAllowed | Shows how many Pods can be evicted right now |
kubectl delete pod | Does NOT respect PDBs (bypasses Eviction API) |
| Anti-pattern | minAvailable = replicas → blocks all drains forever |
📝 Quiz: Pod Disruption Budgets
Q1: A Deployment has 5 replicas. PDB is maxUnavailable: 2. How many Pods can be evicted simultaneously?
Q2: A node crashes, killing 2 Pods. The PDB says minAvailable: 3 and there were 5 Pods. Does the PDB prevent this?
Q3: You run kubectl drain node-1 but it hangs. kubectl get pdb shows ALLOWED DISRUPTIONS: 0. Why and how do you fix it?
kubectl get pods -l app=web. (2) Fix the unhealthy Pod (or delete it if stuck). (3) Once a replacement becomes Ready, disruptionsAllowed goes back up and the drain proceeds.Q4: What's the difference between kubectl drain and kubectl delete pod regarding PDBs?
kubectl drain uses the Eviction API which checks PDBs — it will wait/retry if the budget doesn't allow eviction.kubectl delete pod bypasses the Eviction API entirely — it directly deletes the Pod regardless of any PDB. Never use delete pod for operational drains.Q5: You have a 3-replica etcd cluster. What PDB configuration ensures quorum is always maintained during drains?
minAvailable: 2 (quorum for 3-node Raft). This ensures at least 2 etcd Pods are always up, allowing only 1 to be evicted at a time. Drains proceed one node at a time, maintaining consensus throughout.Q6: A PDB has minAvailable: "80%" and the Deployment has 4 replicas. How many disruptions are allowed?