Pod Affinity and Anti-Affinity in Kubernetes Explained Simply
Learn Kubernetes pod affinity and anti-affinity with clear examples — required vs preferred rules, topologyKey, spreading replicas across zones, and co-locating pods for performance.
When you run multiple replicas of an app in Kubernetes, the scheduler puts them wherever there is room. That might mean all 3 replicas land on the same node — and if that node dies, your app goes down completely. Pod affinity and anti-affinity solve this.
The Problem Pod Affinity Solves
Without affinity rules, the Kubernetes scheduler optimizes for bin-packing (fitting pods where resources are available). This is efficient but fragile:
- All replicas on one node = single point of failure
- App pod and its Redis cache on opposite nodes = network latency on every request
- Pods from different tenants sharing sensitive nodes = noisy neighbor or compliance risk
Pod affinity lets you say "put this pod near that pod." Pod anti-affinity lets you say "keep this pod away from that pod."
Node Affinity vs Pod Affinity
These are different features — easy to confuse:
- Node affinity: schedule this pod onto nodes with specific labels (
disktype=ssd,zone=ap-south-1a) - Pod affinity: schedule this pod on a node where another specific pod is already running
- Pod anti-affinity: schedule this pod on a node where a specific pod is NOT running
Required vs Preferred Rules
Both affinity and anti-affinity support two modes:
requiredDuringSchedulingIgnoredDuringExecution— hard rule: pod will not schedule at all if rule cannot be satisfiedpreferredDuringSchedulingIgnoredDuringExecution— soft rule: scheduler tries to honor it, but will schedule anyway if it cannot
"IgnoredDuringExecution" means: once the pod is running, if the rule stops being satisfied (e.g., the matching pod moves), the running pod is NOT evicted. Kubernetes has a future RequiredDuringExecution mode planned but it is not GA yet.
topologyKey: The Key Concept
topologyKey defines the scope of spreading. It is a node label that groups nodes together.
Common values:
kubernetes.io/hostname— each individual node is a unique topology domaintopology.kubernetes.io/zone— each availability zone is a topology domaintopology.kubernetes.io/region— each region is a topology domain
When you say "spread across topology.kubernetes.io/zone", Kubernetes treats all nodes in zone ap-south-1a as one unit and all nodes in ap-south-1b as another. It will place replicas in different zones.
Example 1: Spread Replicas Across Zones (Anti-Affinity)
You have a 3-replica deployment and you want no two replicas on the same availability zone.
apiVersion: apps/v1
kind: Deployment
metadata:
name: web-app
spec:
replicas: 3
selector:
matchLabels:
app: web-app
template:
metadata:
labels:
app: web-app
spec:
affinity:
podAntiAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
- labelSelector:
matchExpressions:
- key: app
operator: In
values:
- web-app
topologyKey: topology.kubernetes.io/zone
containers:
- name: web
image: nginx:1.25What this does: the scheduler looks at running pods with label app=web-app. For each new pod, it refuses to place it in a zone where such a pod already exists. Result: replicas are spread across zones.
Important: if you only have 2 zones but 3 replicas, the required rule will leave the 3rd replica in Pending state. Use preferred if you want best-effort spreading:
podAntiAffinity:
preferredDuringSchedulingIgnoredDuringExecution:
- weight: 100
podAffinityTerm:
labelSelector:
matchExpressions:
- key: app
operator: In
values:
- web-app
topologyKey: topology.kubernetes.io/zoneThe weight (1–100) tells the scheduler how much to prefer this rule vs other rules.
Example 2: Co-locate App and Cache (Affinity)
Your app makes thousands of Redis calls per second. You want the app pod to always land on the same node as a Redis pod to minimize latency.
apiVersion: apps/v1
kind: Deployment
metadata:
name: api-server
spec:
replicas: 2
selector:
matchLabels:
app: api-server
template:
metadata:
labels:
app: api-server
spec:
affinity:
podAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
- labelSelector:
matchExpressions:
- key: app
operator: In
values:
- redis-cache
topologyKey: kubernetes.io/hostname
containers:
- name: api
image: myapp:1.0The api-server pod will only schedule on a node that already has a pod with app=redis-cache running on it. If no such node exists, the pod stays Pending.
Make sure you deploy Redis first (or use a preferred rule to avoid chicken-and-egg issues).
Example 3: Spread Across Nodes (Simpler Anti-Affinity)
For a simple HA setup — no two replicas on the same physical node:
affinity:
podAntiAffinity:
preferredDuringSchedulingIgnoredDuringExecution:
- weight: 100
podAffinityTerm:
labelSelector:
matchLabels:
app: web-app
topologyKey: kubernetes.io/hostnameThis is the most common pattern for production deployments.
How It Affects Scheduling
Check if anti-affinity is causing scheduling failures:
kubectl describe pod web-app-xyz | grep -A 10 "Events:"
# Look for: "0/3 nodes are available: 3 node(s) didn't match pod anti-affinity rules"
kubectl get pods -o wide
# Check which nodes each replica landed onIf pods are Pending due to anti-affinity, your options are:
- Add more nodes
- Switch from
requiredtopreferred - Reduce replica count to match available topology domains
Pod Affinity vs TopologySpreadConstraints
Kubernetes 1.19+ introduced topologySpreadConstraints as a cleaner way to spread pods:
topologySpreadConstraints:
- maxSkew: 1
topologyKey: topology.kubernetes.io/zone
whenUnsatisfiable: DoNotSchedule
labelSelector:
matchLabels:
app: web-appmaxSkew: 1 means the difference in pod count between any two zones must not exceed 1. This is simpler than writing anti-affinity rules for spreading use cases.
Use anti-affinity when you need to co-locate with or exclude specific OTHER pods (cross-app rules). Use topologySpreadConstraints when you just want to spread replicas of the same app evenly.
Summary
| Rule | Use when |
|---|---|
podAntiAffinity + hostname | No two replicas on same node |
podAntiAffinity + zone | Spread replicas across AZs |
podAffinity + hostname | Co-locate app with its cache/sidecar |
required | Strict HA — fail scheduling if rule unmet |
preferred | Best-effort — always schedule, try to honor rule |
topologySpreadConstraints | Even distribution, simpler syntax |
Start with preferredDuringScheduling + topologyKey: kubernetes.io/hostname for most production workloads. That alone prevents all replicas landing on a single node without risking Pending pods.
Learn more: Kubernetes docs on affinity. Practice on a real cluster with KillerCoda Kubernetes labs (free).
Today I Fixed
Short real fixes from production — posted daily
Stay ahead of the curve
Get the latest DevOps, Kubernetes, AWS, and AI/ML guides delivered straight to your inbox. No spam — just practical engineering content.
Related Articles
Build a Kubernetes Cluster with kubeadm from Scratch (2026)
Step-by-step guide to building a real multi-node Kubernetes cluster using kubeadm — no managed services, no shortcuts.
How to Build a DevOps Home Lab for Free in 2026
You don't need expensive hardware to practice DevOps. Here's how to build a complete home lab with Kubernetes, CI/CD, and monitoring using free tools and cloud free tiers.
How to Crack the CKA Exam in 2026: Study Plan, Resources, and Tips
Complete CKA exam prep guide for 2026 — what to study, how to practice, which resources actually help, and tips to pass on the first attempt.