Skip to main content
RunBook Academy

KubernetesXXV · Topology Spread ConstraintsTopology spread

Topology spread vs anti-affinity — choosing the right tool

Advanced⏱ ~17 minkubectlkubeadm

What you'll learn

  • Compare topology spread and pod anti-affinity in detail
  • Choose the right tool for a workload class
  • Combine both for layered protection
  • Identify when each is the wrong tool

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.

Topology spread and pod anti-affinity both place Pods across domains, but they have different goals and failure modes. Topology spread is for balanced distribution; anti-affinity is for strict separation. This lesson covers the comparison, the trade-offs, and the patterns that combine both.

The comparison

flowchart TB
    A{Distribution goal}
    A -->|Balanced distribution<br/>tolerate some skew| B[Topology spread]
    A -->|Strict separation<br/>no co-location| C[Pod anti-affinity]
    B --> D["Cheaper<br/>counts Pods"]
    C --> E["Expensive<br/>evaluates selectors"]
AspectTopology spreadPod anti-affinity
GoalBalanced distributionStrict separation
MechanismCount Pods per domainEvaluate selector per Pod
CostO(P) countingO(N × P) selector walk
SkewBounded by maxSkewNone (no co-location)
Failure modeScheduleAnyway continuesPending Pod
EvictionNoneNone (standard variant)

Cost comparison

flowchart LR
    A[Topology spread] --> B["Walk all Pods<br/>once per scheduling<br/>count per domain"]
    C[Pod anti-affinity] --> D["Walk all Pods<br/>on every node<br/>evaluate selector"]
    B --> E["Cost: O(P)"]
    D --> F["Cost: O(N × P)"]

For a cluster with 1000 Pods and 100 nodes:

  • Topology spread: ~1000 operations (one walk through Pod cache).
  • Anti-affinity: ~100,000 operations (100 nodes × ~1000 Pods each).

The difference is significant for large clusters.

Failure modes

flowchart TB
    A[Constraint cannot be satisfied] --> B{Topology spread}
    A --> C{Pod anti-affinity}
    B -->|ScheduleAnyway| D[Schedule with skew]
    B -->|DoNotSchedule| E[Pending]
    C -->|required| E
    C -->|preferred| D

Topology spread with ScheduleAnyway always succeeds. Anti-affinity with required is hard; if no node satisfies, the Pod is Pending.

The decision matrix

RequirementUse topology spreadUse anti-affinity
Spread replicas evenlyYesNo
Strict separation (no co-location)NoYes
Tolerate some imbalanceYesNo
Cost-sensitive schedulingYesNo
Per-version distributionYes (with matchLabelKeys)No
flowchart TB
    A{Strict or balanced?}
    A -->|Strict| B[Anti-affinity]
    A -->|Balanced| C[Topology spread]
    B --> D{Always avoid co-location?}
    D -->|yes| E[Required anti-affinity]
    D -->|no, prefer separation| F[Preferred anti-affinity]
    C --> G{ScheduleAnyway OK?}
    G -->|yes| H["Topology spread<br/>ScheduleAnyway"]
    G -->|no, must balance| I["Topology spread<br/>DoNotSchedule"]

Pattern: combine both

spec:
  affinity:
    podAntiAffinity:
      requiredDuringSchedulingIgnoredDuringExecution:
      - topologyKey: kubernetes.io/hostname
        labelSelector:
          matchLabels:
            app: db
  topologySpreadConstraints:
  - maxSkew: 1
    topologyKey: topology.kubernetes.io/zone
    whenUnsatisfiable: DoNotSchedule
    labelSelector:
      matchLabels:
        app: db

The Pod must not co-locate with another db Pod on the same node (strict, hard). The Pod’s zone distribution must be balanced (soft, maxSkew 1).

flowchart TB
    A[Pod arrives] --> B{Anti-affinity:<br/>same node?}
    B -->|yes| C[Eliminated]
    B -->|no| D{Topology spread:<br/>zone skew?}
    D -->|yes| E["Eliminated or<br/>ScheduleAnyway"]
    D -->|no| F[Schedule]

When to reach for each

Use topology spread when:

  • Spreading replicas evenly. The standard pattern for HA workloads.
  • Tolerating some imbalance. A ScheduleAnyway constraint with maxSkew: 2 tolerates imbalance.
  • Per-version distribution during rollouts. With matchLabelKeys, each ReplicaSet distributes independently.
  • Cost-sensitive scheduling. Topology spread is cheaper than anti-affinity.

Use anti-affinity when:

  • Strict separation. Even one co-located Pod is unacceptable (e.g., a primary and a replica that must not share a node).
  • Compliance. Regulatory requirements that mandate separation.
  • Hardware-specific. A workload that requires different hardware from another workload (e.g., a primary on fast disk and a replica on slow disk).

The anti-patterns

Anti-pattern 1: anti-affinity for “spread replicas”

# WRONG: too expensive for the goal
affinity:
  podAntiAffinity:
    requiredDuringSchedulingIgnoredDuringExecution:
    - topologyKey: kubernetes.io/hostname
      labelSelector:
        matchLabels:
          app: web

The goal is “spread replicas.” Use topology spread:

# RIGHT: cheaper, configurable skew
topologySpreadConstraints:
- maxSkew: 1
  topologyKey: kubernetes.io/hostname
  whenUnsatisfiable: ScheduleAnyway
  labelSelector:
    matchLabels:
      app: web

Anti-pattern 2: topology spread for strict separation

# WRONG: topology spread tolerates skew
topologySpreadConstraints:
- maxSkew: 1
  topologyKey: kubernetes.io/hostname
  whenUnsatisfiable: DoNotSchedule

The goal is “no two Pods on the same node.” Topology spread with maxSkew: 1 allows one extra Pod per domain. Use anti-affinity:

# RIGHT: strict separation
affinity:
  podAntiAffinity:
    requiredDuringSchedulingIgnoredDuringExecution:
    - topologyKey: kubernetes.io/hostname
      labelSelector:
        matchLabels:
        app: db

Anti-pattern 3: both at once

# WRONG: redundant
topologySpreadConstraints:
- maxSkew: 1
  topologyKey: kubernetes.io/hostname
  whenUnsatisfiable: DoNotSchedule
  labelSelector:
    matchLabels:
      app: web
affinity:
  podAntiAffinity:
    requiredDuringSchedulingIgnoredDuringExecution:
    - topologyKey: kubernetes.io/hostname
      labelSelector:
        matchLabels:
        app: web

Topology spread with maxSkew: 1, DoNotSchedule is equivalent to required anti-affinity at the same domain. Pick one, not both.

Production patterns

Pattern 1: HA web (topology spread)

topologySpreadConstraints:
- maxSkew: 1
  topologyKey: kubernetes.io/hostname
  whenUnsatisfiable: ScheduleAnyway
  labelSelector:
    matchLabels:
      app: web

Standard spread across nodes. Allows some imbalance via ScheduleAnyway.

Pattern 2: HA database (anti-affinity)

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

A database with strict separation. Each replica on its own node.

Pattern 3: HA layered (both)

affinity:
  podAntiAffinity:
    requiredDuringSchedulingIgnoredDuringExecution:
    - topologyKey: kubernetes.io/hostname
      labelSelector:
        matchLabels:
          app: db
topologySpreadConstraints:
- maxSkew: 1
  topologyKey: topology.kubernetes.io/zone
  whenUnsatisfiable: DoNotSchedule
  labelSelector:
    matchLabels:
      app: db

Each replica on its own node; replicas balanced across zones.

Quiz

Knowledge check · 4 questions

  1. Q1. For most spread-replicas use cases, which is the right tool?

  2. Q2. Topology spread and required pod anti-affinity are equivalent for spreading replicas.

  3. Q3. Your team uses topology spread with maxSkew 1 and ScheduleAnyway for a 100-replica Deployment on a 10-node cluster. The distribution is imbalanced (some nodes have 12 Pods, others have 8). Diagnose.

    Deployment with topology spread maxSkew 1 ScheduleAnyway. Cluster has 10 nodes. Some nodes have 12 Pods; others have 8.

  4. Q4. When is required pod anti-affinity the right tool, and when is topology spread better?

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

Production discipline

  • Default to topology spread. It’s cheaper and more flexible than anti-affinity.
  • Use anti-affinity for hard separation. Compliance, hardware, correctness.
  • Combine for layered protection. Anti-affinity for node-level; topology spread for zone-level.
  • Don’t use both for the same domain. Redundant and confusing.
  • Audit the resulting distribution. A dashboard that surfaces per-domain Pod counts catches imbalance.

The choice between topology spread and anti-affinity is the choice between balanced and strict. Operators who choose deliberately have workloads that distribute predictably.