KubernetesXXV · Topology Spread ConstraintsTopology spread
labelSelector and matchLabelKeys — dynamic grouping
What you'll learn
- Configure labelSelector for static topology spread
- Configure matchLabelKeys for dynamic grouping
- Distinguish static and dynamic constraints
- Apply matchLabelKeys for versioned deployments and per-tenant isolation
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
labelSelector is the static form: the Pods to count are
those matching a fixed selector (e.g., app: web).
matchLabelKeys (introduced in Kubernetes 1.27) is the
dynamic form: the Pods to count are those matching by label
keys, allowing per-version or per-tenant grouping. This
lesson covers both forms, the trade-offs, and the
production patterns.
labelSelector — static
topologySpreadConstraints:
- maxSkew: 1
topologyKey: kubernetes.io/hostname
whenUnsatisfiable: DoNotSchedule
labelSelector:
matchLabels:
app: web
The Pods to count are those with app: web. The selector
is static; changing it requires re-applying the manifest.
flowchart LR
A[Pod with constraint] --> B["Count Pods with<br/>app: web per domain"]
B --> C[web-1, web-2, web-3...]
C --> D[Skew check]
D --> E[Schedule or Pending]
matchLabelKeys — dynamic
topologySpreadConstraints:
- maxSkew: 1
topologyKey: kubernetes.io/hostname
whenUnsatisfiable: DoNotSchedule
matchLabelKeys:
- pod-template-hash
The Pods to count are those whose pod-template-hash
label matches the Pod’s own pod-template-hash. This is
the dynamic form: the constraint applies per version
(per ReplicaSet) automatically.
flowchart LR
A[Pod arrives] --> B["Read pod-template-hash<br/>= abc123"]
B --> C["Count Pods with<br/>pod-template-hash=abc123<br/>per domain"]
C --> D[Skew check]
D --> E[Schedule]
When to use which
flowchart TB
A{Constraint type}
A -->|Same selector always| B[labelSelector]
A -->|Different per version<br/>or per-tenant| C[matchLabelKeys]
B --> D[Static, simple]
C --> E[Dynamic, versioned]
Use labelSelector when… | Use matchLabelKeys when… |
|---|---|
The selector is fixed (e.g., app: web) | You want per-version distribution during rollouts |
| Simple configurations | You want per-tenant or per-customer isolation |
| Single-version deployments | You want to count only “current” Pods, not old ones |
matchLabelKeys in detail
matchLabelKeys:
- pod-template-hash
- app.kubernetes.io/version
The constraint counts Pods whose values for these label
keys match the new Pod’s values. With
pod-template-hash, each ReplicaSet’s Pods are counted
separately.
flowchart TB
A["Old RS:<br/>pod-template-hash=abc"] --> B["Counted in<br/>distribution"]
C["New RS:<br/>pod-template-hash=def"] --> D["Counted in<br/>distribution"]
B --> E[Separate count]
D --> E
Without matchLabelKeys, the constraint counts all Pods
with app: web regardless of version. During a rollout,
old and new Pods are counted together; the constraint
applies to the union.
With matchLabelKeys: [pod-template-hash], each version’s
distribution is independent. The new ReplicaSet’s Pods
distribute across nodes without considering the old
ReplicaSet’s Pods.
Production patterns
Pattern 1: per-version distribution during rollouts
topologySpreadConstraints:
- maxSkew: 1
topologyKey: kubernetes.io/hostname
whenUnsatisfiable: DoNotSchedule
matchLabelKeys:
- pod-template-hash
During a rollout, each version’s Pods distribute across nodes. The old version stays where it was; the new version spreads to its own nodes.
flowchart TB
subgraph "Old RS: web-v1"
N1[node-01] --> P1[web-1]
N2[node-02] --> P2[web-2]
N3[node-03] --> P3[web-3]
end
subgraph "New RS: web-v2"
N4[node-01] --> P4[web-4]
N5[node-02] --> P5[web-5]
N6[node-03] --> P6[web-6]
end
Pattern 2: per-tenant isolation
topologySpreadConstraints:
- maxSkew: 1
topologyKey: kubernetes.io/hostname
whenUnsatisfiable: DoNotSchedule
matchLabelKeys:
- tenant
Pods are grouped by tenant. Each tenant’s Pods distribute across nodes; different tenants may co-locate on the same node.
flowchart TB
N1[node-01] --> P1A["Tenant A: pod-1"]
N1 --> P1B["Tenant B: pod-1"]
N2[node-02] --> P2A["Tenant A: pod-2"]
N2 --> P2B["Tenant B: pod-2"]
Pattern 3: per-tier distribution
topologySpreadConstraints:
- maxSkew: 1
topologyKey: topology.kubernetes.io/zone
whenUnsatisfiable: DoNotSchedule
matchLabelKeys:
- app.kubernetes.io/version
Each version’s Pods distribute across zones. During a rollout, the new version spreads to all zones; the old version stays put.
Combining with labelSelector
topologySpreadConstraints:
- maxSkew: 1
topologyKey: kubernetes.io/hostname
whenUnsatisfiable: DoNotSchedule
labelSelector:
matchLabels:
app: web
matchLabelKeys:
- pod-template-hash
The Pods to count are those with app: web AND matching
the new Pod’s pod-template-hash. Both conditions must
hold.
flowchart TB
A[Pod arrives] --> B{labelSelector:<br/>app=web?}
B -->|yes| C{matchLabelKeys:<br/>same hash?}
C -->|yes| D[Counted]
C -->|no| E[Not counted]
B -->|no| E
D --> F[Skew check]
Failure modes
Failure 1: missing label key
matchLabelKeys:
- my-pod-template-hash # typo
The Pods to count are those with my-pod-template-hash
matching the new Pod’s. If the new Pod doesn’t have this
label (because the typo means it’s never set), no Pods
match. The constraint has no effect.
The fix: verify the label exists on Pods and matches exactly.
Failure 2: matchLabelKeys too narrow
matchLabelKeys:
- pod-template-hash
- app.kubernetes.io/version
- tenant
Three keys. The Pods to count must match all three on the new Pod. If any one is missing or different, the Pod is not counted. This may make the distribution too narrow.
The fix: include only the keys that define the grouping.
Failure 3: dynamic with static expected
A manifest designed for static labelSelector may behave
unexpectedly with matchLabelKeys. The constraint is
different: per-version counting vs total counting.
Quiz
Knowledge check · 4 questions
Q1. What does matchLabelKeys enable in topology spread?
Q2. matchLabelKeys is an alpha feature that should not be used in production.
Q3. Your team uses topology spread with matchLabelKeys pod-template-hash. During a rollout, the new ReplicaSet's Pods cluster on the same nodes as the old ReplicaSet. Diagnose.
Deployment has matchLabelKeys pod-template-hash. During a rollout, the new ReplicaSet's 5 Pods cluster on 2 nodes instead of spreading to 5 nodes.
Q4. Explain how matchLabelKeys enables per-version distribution during rollouts.
Passing score: 75%. Answers are checked in this browser.
Production discipline
- Use matchLabelKeys for rollouts. Each version’s Pods distribute independently; old and new do not interfere.
- Use labelSelector for stable workloads. A workload with a fixed identity (e.g., a StatefulSet) uses static selectors.
- Combine for filtering.
labelSelectordefines the broad set;matchLabelKeysdefines the grouping. - Audit the resulting distribution. A dashboard that surfaces per-version Pod counts catches distribution failures.
- Test with rollouts. A rollout that produces uneven distribution is a signal that the constraint is wrong.
matchLabelKeys enables dynamic grouping for production patterns. Operators who use it deliberately have rollouts that distribute predictably.