Skip to main content
RunBook Academy

KubernetesXXIV · Pod Affinity and Anti-AffinityPod affinity

Pod affinity and anti-affinity — co-locate or separate Pods

Advanced⏱ ~17 minkubectlkubeadm

What you'll learn

  • Describe Pod affinity and Pod anti-affinity in Kubernetes
  • Configure the topologyKey and labelSelector fields
  • Distinguish required and preferred variants
  • Reason about the performance cost of large topology domains

Prerequisites

Verified against Kubernetes 1.34.x · kubeadm 1.34.x · kubectl 1.34.x · etcd 3.6.x · CoreDNS 1.11.x · containerd 1.7.x / 2.x · 2026-08-16

Not yet marked complete on this device.

Node Affinity is “this Pod wants this node.” Pod affinity and anti-affinity are “this Pod wants (or doesn’t want) Pods with these labels on the same (or different) node.” This is the placement tool for relationships between Pods: co-locate a cache with the workload that uses it; separate replicas across nodes for availability. This lesson covers the syntax, the topologyKey, and the required/preferred variants.

The shape

spec:
  affinity:
    podAffinity:
      requiredDuringSchedulingIgnoredDuringExecution:
      - topologyKey: kubernetes.io/hostname
        labelSelector:
          matchLabels:
            app: cache
    podAntiAffinity:
      requiredDuringSchedulingIgnoredDuringExecution:
      - topologyKey: kubernetes.io/hostname
        labelSelector:
          matchLabels:
            app: web

The structure:

  • podAffinity — co-locate with Pods matching the selector.
  • podAntiAffinity — separate from Pods matching the selector.
  • topologyKey — the placement domain. kubernetes.io/ hostname means “same node”; topology.kubernetes.io/ zone means “same zone.”
  • labelSelector — the labels of the Pods to consider.
flowchart TB
    A["Pod with affinity/anti-affinity"] --> B["topologyKey:<br/>kubernetes.io/hostname"]
    B --> C{Label Selector:<br/>match?}
    C -->|yes| D{Anti-affinity?<br/>co-locate?}
    D -->|yes| E[Co-locate]
    D -->|no| F[Separate]

Pod affinity — co-locate

spec:
  affinity:
    podAffinity:
      requiredDuringSchedulingIgnoredDuringExecution:
      - topologyKey: kubernetes.io/hostname
        labelSelector:
          matchLabels:
            app: cache

The Pod must land on a node that already hosts a Pod labelled app: cache. Used for workloads that benefit from local communication (cache, sidecar, agent).

flowchart LR
    A[Web Pod] -->|podAffinity:<br/>app=cache| B[Node with cache Pod]
    A --> C[Node without cache Pod]
    C -.->|rejected| A
    B --> D[Co-located]

Pod anti-affinity — separate

spec:
  affinity:
    podAntiAffinity:
      requiredDuringSchedulingIgnoredDuringExecution:
      - topologyKey: kubernetes.io/hostname
        labelSelector:
          matchLabels:
            app: web

The Pod must not land on a node that already hosts a Pod labelled app: web. Used for spreading replicas across nodes for availability.

flowchart LR
    A[Web replica] -->|podAntiAffinity:<br/>app=web| B[Node without web Pod]
    A --> C[Node with web Pod]
    C -.->|rejected| A
    B --> D[Spread]

Topology key

The topologyKey field is a node label that defines the placement domain:

Topology keyDomain
kubernetes.io/hostnameSame node
topology.kubernetes.io/zoneSame zone
topology.kubernetes.io/regionSame region
topology.kubernetes.io/zone (legacy)Same zone (legacy)

For kubernetes.io/hostname, the affinity is “co-locate on the same node.” For topology.kubernetes.io/zone, the affinity is “co-locate in the same zone (any node in the zone).”

flowchart LR
    A[Pod with affinity] --> B{topologyKey}
    B -->|hostname| C[Same node]
    B -->|zone| D[Same zone]
    B -->|region| E[Same region]

Required vs preferred

podAffinity:
  requiredDuringSchedulingIgnoredDuringExecution:
  - topologyKey: kubernetes.io/hostname
    labelSelector:
      matchLabels:
        app: cache

Required: if no node satisfies, the Pod is unschedulable.

podAffinity:
  preferredDuringSchedulingIgnoredDuringExecution:
  - weight: 80
    podAffinityTerm:
      topologyKey: kubernetes.io/hostname
      labelSelector:
        matchLabels:
          app: cache

Preferred: if no node satisfies, the Pod schedules anyway. The weight (1-100) is added to the score for nodes that do satisfy.

flowchart TB
    A[Affinity type] -->|required| B["If no match: Pending"]
    A -->|preferred| C["If no match: schedule anywhere"]
    C --> D["Match: score += weight"]

Naming spaces

Pod affinity operates within the same namespace by default. The selector only matches Pods in the same namespace. For cross-namespace affinity:

podAffinity:
  requiredDuringSchedulingIgnoredDuringExecution:
  - topologyKey: kubernetes.io/hostname
    namespaces: ["prod",,"staging"]  # list of namespaces
    labelSelector:
      matchLabels:
        app: cache

The namespaces field (an array) lists the namespaces to consider. namespaces: [] (empty list with the explicit-namespace form) means “all namespaces”; but the default behaviour is to consider only the Pod’s own namespace.

The performance cost

flowchart LR
    A[Scheduler receives Pod] --> B[Filter phase]
    B --> C[Affinity filter]
    C --> D{For each node...}
    D --> E{For each Pod on node...}
    E --> F{Selector matches?}
    F --> G{Constraint satisfied?}

Pod affinity requires the scheduler to walk every Pod on every candidate node, evaluating the selector. For large clusters (thousands of Pods per node), the cost is significant.

The scheduler caches Pods; the affinity evaluation is amortised. But the first scheduling of a Pod with broad affinity can be slow.

Production patterns

Pattern 1: co-locate with cache

affinity:
  podAffinity:
    requiredDuringSchedulingIgnoredDuringExecution:
    - topologyKey: kubernetes.io/hostname
      labelSelector:
        matchLabels:
          app: cache

A web Pod that depends on a local cache. Co-location reduces network latency.

Pattern 2: spread replicas

affinity:
  podAntiAffinity:
    requiredDuringSchedulingIgnoredDuringExecution:
    - topologyKey: kubernetes.io/hostname
      labelSelector:
        matchLabels:
          app: web

A Deployment’s Pods spread across nodes. With replicas: 6 and 6 nodes, each node gets one Pod.

Pattern 3: spread across zones

affinity:
  podAntiAffinity:
    requiredDuringSchedulingIgnoredDuringExecution:
    - topologyKey: topology.kubernetes.io/zone
      labelSelector:
        matchLabels:
          app: db

A database’s replicas spread across zones. A zone failure loses one replica; the others continue.

Failure modes

Failure 1: no node satisfies required

flowchart TB
    A[Required affinity] --> B{Any node<br/>satisfies?}
    B -->|no| C[Pod Pending]
    B -->|yes| D[Scheduled]

The fix: change to preferred, add more nodes, or remove the constraint.

Failure 2: broad selector

A selector that matches every Pod requires walking every Pod. Performance degrades. The fix: narrow the selector.

Failure 3: cross-namespace without RBAC

A Pod with namespaces: ["kube-system"] requires the scheduler to read Pods in kube-system. By default, the scheduler has access; in some restricted setups, it does not. The fix: verify the scheduler’s RBAC.

Quiz

Knowledge check · 4 questions

  1. Q1. What does topologyKey define in Pod affinity?

  2. Q2. Pod affinity operates across namespaces by default.

  3. Q3. Your team uses Pod affinity with a selector matching all web Pods across 1000 nodes. Scheduling is slow. Diagnose.

    Pod affinity with labelSelector app web. Cluster has 1000 nodes and 5000 web Pods. Scheduling each Pod takes seconds.

  4. Q4. Explain the performance cost of Pod affinity and the patterns that mitigate it.

Passing score: 75%. Answers are checked in this browser.

Production discipline

  • Keep selectors tight. A selector matching one or two labels is fast; a selector matching all Pods is slow.
  • Prefer hostname over zone. Hostname affinity is cheaper; zone affinity walks Pods across many nodes.
  • Use required for hard separation. A Deployment’s replicas must spread across nodes; required anti-affinity enforces.
  • Use preferred for soft preference. Co-location with a cache is preferred, not required; the workload runs anywhere.
  • Audit the resulting placement. A dashboard that surfaces per-node Pod counts catches affinity failures (Pods clustered on one node).

Pod affinity and anti-affinity are the placement tools for relationships between Pods. Operators who understand the topologyKey and selector semantics have workloads that land predictably.