Node affinity controls which nodes a Pod lands on. Pod affinity controls whether a Pod should be co-located with or separated from other Pods. It answers: "Place me near Pods running X" (affinity) or "Keep me away from Pods running Y" (anti-affinity).

1. The Topology Key Concept

Pod affinity doesn't just say "near another Pod" — it defines what "near" means using a topology key. The topology key is a node label that groups nodes into domains.

Zone us-east-1a Node 1 Node 2 Cache Pod Zone us-east-1b Node 3 Node 4 New Web Pod affinity: near Cache Pod topologyKey: zone → Scheduled in zone a (same zone as Cache Pod)

Common Topology Keys

topologyKey"Same" MeansUse Case
kubernetes.io/hostnameSame nodeCo-locate on exact same machine (shared memory, local comms)
topology.kubernetes.io/zoneSame availability zoneReduce cross-zone latency/cost
topology.kubernetes.io/regionSame regionData sovereignty, compliance
The topology key defines the "blast radius" of affinity.
hostname = "co-locate on the exact same node" (tightest)
zone = "co-locate in the same zone" (medium — nodes in same AZ)
region = "co-locate in the same region" (loosest)
Anti-affinity works the same: "keep apart per-node" vs "keep apart per-zone."

2. Pod Affinity — "Schedule Near"

Required (Hard)

# "I MUST be in the same zone as a Pod labeled app=cache"
spec:
  affinity:
    podAffinity:
      requiredDuringSchedulingIgnoredDuringExecution:
        - labelSelector:
            matchExpressions:
              - key: app
                operator: In
                values: ["cache"]
          topologyKey: topology.kubernetes.io/zone

Preferred (Soft)

# "I'd LIKE to be on the same node as a Pod labeled app=cache"
spec:
  affinity:
    podAffinity:
      preferredDuringSchedulingIgnoredDuringExecution:
        - weight: 80
          podAffinityTerm:
            labelSelector:
              matchExpressions:
                - key: app
                  operator: In
                  values: ["cache"]
            topologyKey: kubernetes.io/hostname

Use Cases for Pod Affinity

ScenariotopologyKeyRationale
Web server near its Redis cachezoneReduce cache latency (same AZ)
App Pod near its databasezoneReduce cross-AZ data transfer cost
Sidecar-style co-locationhostnameMust be on same node (shared volume, localhost comms)
ML training near datahostnameGPU + data locality for performance
Use Pod affinity with zone topology to colocate services that communicate frequently. In AWS/GCP, cross-AZ traffic costs ~$0.01/GB. A web service making 1000 req/s to a cache in another AZ generates significant cost. Pod affinity with zone topology eliminates this.

3. Pod Anti-Affinity — "Schedule Away From"

Required — Spread Across Zones

# "I MUST NOT be in the same zone as another Pod with app=web"
spec:
  affinity:
    podAntiAffinity:
      requiredDuringSchedulingIgnoredDuringExecution:
        - labelSelector:
            matchExpressions:
              - key: app
                operator: In
                values: ["web"]
          topologyKey: topology.kubernetes.io/zone
# If you have 3 replicas and 3 zones, each replica lands in a different zone
# If you have 4 replicas and 3 zones → 4th Pod is UNSCHEDULABLE (Pending!)

Required — One Per Node

# "Only one instance of me per node" (like a DaemonSet but with replicas control)
spec:
  affinity:
    podAntiAffinity:
      requiredDuringSchedulingIgnoredDuringExecution:
        - labelSelector:
            matchLabels:
              app: web
          topologyKey: kubernetes.io/hostname
# Ensures at most 1 "app=web" Pod per node
# If replicas > nodes → extra Pods stay Pending

Preferred — Spread Across Nodes (Best Effort)

# "Try to spread across nodes, but don't fail if you can't"
spec:
  affinity:
    podAntiAffinity:
      preferredDuringSchedulingIgnoredDuringExecution:
        - weight: 100
          podAffinityTerm:
            labelSelector:
              matchLabels:
                app: web
            topologyKey: kubernetes.io/hostname
# Scheduler spreads Pods across nodes but will colocate if not enough nodes
Required anti-affinity can cause unschedulable Pods. If you require "one per zone" but have more replicas than zones, the excess Pods stay Pending forever. Always verify: replicas ≤ number of topology domains. Or use preferred to degrade gracefully instead of failing.

Common Anti-Affinity Patterns

PatterntopologyKeyRequired/PreferredWhy
HA across zoneszoneRequired (if replicas ≤ zones)Survive zone failure
Spread across nodeshostnamePreferred (safe)Survive node failure
Separate competing workloadshostnameRequiredPrevent resource contention
Keep replicas of same DB apartzoneRequiredPrevent correlated failure
The most common production pattern: required anti-affinity with zone topology for databases/stateful apps (must survive zone failure) + preferred anti-affinity with hostname for stateless services (spread across nodes but don't block scheduling if nodes are limited).

4. Scalability Warning

Pod affinity/anti-affinity is expensive for the scheduler. For every Pod being scheduled, the scheduler must check ALL other Pods in the cluster (or namespace) to see which topology domains contain matching Pods.

# Complexity: O(Pods × Nodes)
# With 1000 Pods and 100 nodes, the scheduler evaluates 100,000 combinations
# This is why large clusters see scheduling latency increase with pod affinity

Mitigation Strategies

  • Use namespaces field — restrict the search to specific namespaces (instead of all)
  • Use namespaceSelector — limit which namespaces are checked
  • Prefer Topology Spread Constraints — lighter-weight for spreading (next lesson)
  • Use preferred over required — fails gracefully instead of blocking
# Restrict affinity search to specific namespaces:
podAffinity:
  requiredDuringSchedulingIgnoredDuringExecution:
    - labelSelector:
        matchLabels:
          app: cache
      topologyKey: topology.kubernetes.io/zone
      namespaces: ["caching"]         # Only look at Pods in this namespace
      # OR:
      namespaceSelector:
        matchLabels:
          team: platform              # Only namespaces with this label

5. Pod Affinity vs Topology Spread Constraints

FeaturePod Anti-AffinityTopology Spread Constraints
GoalBinary: "not on same topology domain"Even distribution: "max skew of N across domains"
Scheduler costHigh (checks all Pods)Lower (built-in optimization)
FlexibilityAll-or-nothing (required) or best-effort (preferred)Tunable maxSkew (allow some imbalance)
Use case"Exactly 1 per zone" / "never colocate with X""Spread evenly across zones (some imbalance OK)"
Recommended forSmall deployments, strict requirementsLarge deployments, general HA spreading
For general "spread my replicas" needs, prefer Topology Spread Constraints (next lesson). They're more efficient, more flexible (tunable skew), and the recommended approach for large-scale deployments. Use Pod anti-affinity when you need strict "never colocate with a specific other Pod" semantics (not just spreading yourself).

Summary

ConceptKey Point
Pod Affinity"Schedule near Pods matching label X" (attract)
Pod Anti-Affinity"Schedule away from Pods matching label X" (repel)
topologyKeyDefines "same domain": hostname (node), zone, region
RequiredHard — Pod stays Pending if no valid domain. Risk of unschedulable.
PreferredSoft — scheduler optimizes but doesn't block
Anti-affinity to selfCommon pattern: use own labels to spread replicas across zones/nodes
ScalabilityExpensive (O(Pods×Nodes)). Use namespaces field to limit scope.
vs Topology SpreadSpread Constraints are lighter-weight for "spread evenly" patterns

📝 Quiz: Pod Affinity & Anti-Affinity

Q1: A Deployment has 3 replicas with required pod anti-affinity (topologyKey: zone). The cluster has 2 zones. What happens?

2 Pods schedule (one per zone), the 3rd stays Pending. Required anti-affinity means no two Pods with the same label can be in the same zone. With only 2 zones, the 3rd Pod has no valid zone. It remains Pending forever. Fix: use preferred anti-affinity, or add a 3rd zone, or reduce to 2 replicas.

Q2: What's the difference between topologyKey: kubernetes.io/hostname and topologyKey: topology.kubernetes.io/zone in anti-affinity?

hostname: "Don't place two matching Pods on the same node." Multiple Pods can be in the same zone (on different nodes).
zone: "Don't place two matching Pods in the same zone." This is stricter — even if there are many nodes in a zone, only one matching Pod is allowed per zone.

Q3: You want a web Pod to be in the same zone as its Redis cache. How do you configure this?

Add pod affinity to the web Pod:
podAffinity:
  requiredDuringSchedulingIgnoredDuringExecution:
    - labelSelector:
        matchLabels:
          app: redis
      topologyKey: topology.kubernetes.io/zone
The web Pod will only schedule in a zone that already has a Pod labeled app=redis.

Q4: Why is Pod affinity/anti-affinity computationally expensive for the scheduler?

For each Pod being scheduled, the scheduler must scan all existing Pods (or those in specified namespaces) to determine which topology domains contain matching Pods. This is O(existing Pods × candidate nodes). In a cluster with thousands of Pods, this scan is expensive and adds scheduling latency. Topology Spread Constraints use an optimized internal algorithm that's faster.

Q5: A Pod has anti-affinity against itself (labelSelector matches its own labels). It's already running on node-1. A second replica is being scheduled. Can it go on node-1?

Depends on the topologyKey:
hostname: No — the existing Pod is on node-1, so anti-affinity blocks the new Pod from node-1.
zone: No — if node-1 is in zone-a, the new Pod can't go to ANY node in zone-a (including node-1 and other nodes in that zone).

Q6: When should you use Pod anti-affinity vs Topology Spread Constraints?

Anti-affinity: When you need strict "never colocate with a specific other workload" semantics (e.g., two competing databases must not be on the same node). Also for small deployments (≤5 replicas) with strict per-zone requirements.
Topology Spread: When you want "spread my replicas evenly" (e.g., 10 replicas across 3 zones with max 1 difference). Better for large-scale spreading, lower scheduler cost, and allows tunable imbalance (maxSkew).