🎉 DevOps Interview Prep Bundle is live — 1000+ Q&A across 20 topicsGet it →
All Articles

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.

DevOpsBoys5 min read
Share:Tweet

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:

  • requiredDuringSchedulingIgnoredDuringExecutionhard rule: pod will not schedule at all if rule cannot be satisfied
  • preferredDuringSchedulingIgnoredDuringExecutionsoft 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 domain
  • topology.kubernetes.io/zone — each availability zone is a topology domain
  • topology.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.

yaml
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.25

What 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:

yaml
podAntiAffinity:
  preferredDuringSchedulingIgnoredDuringExecution:
    - weight: 100
      podAffinityTerm:
        labelSelector:
          matchExpressions:
            - key: app
              operator: In
              values:
                - web-app
        topologyKey: topology.kubernetes.io/zone

The 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.

yaml
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.0

The 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:

yaml
affinity:
  podAntiAffinity:
    preferredDuringSchedulingIgnoredDuringExecution:
      - weight: 100
        podAffinityTerm:
          labelSelector:
            matchLabels:
              app: web-app
          topologyKey: kubernetes.io/hostname

This is the most common pattern for production deployments.

How It Affects Scheduling

Check if anti-affinity is causing scheduling failures:

bash
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 on

If pods are Pending due to anti-affinity, your options are:

  1. Add more nodes
  2. Switch from required to preferred
  3. Reduce replica count to match available topology domains

Pod Affinity vs TopologySpreadConstraints

Kubernetes 1.19+ introduced topologySpreadConstraints as a cleaner way to spread pods:

yaml
topologySpreadConstraints:
  - maxSkew: 1
    topologyKey: topology.kubernetes.io/zone
    whenUnsatisfiable: DoNotSchedule
    labelSelector:
      matchLabels:
        app: web-app

maxSkew: 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

RuleUse when
podAntiAffinity + hostnameNo two replicas on same node
podAntiAffinity + zoneSpread replicas across AZs
podAffinity + hostnameCo-locate app with its cache/sidecar
requiredStrict HA — fail scheduling if rule unmet
preferredBest-effort — always schedule, try to honor rule
topologySpreadConstraintsEven 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

Browse fixes
Newsletter

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

Comments