The question this answers
Which machine does this pod land on, and what do I read when the answer is "none of them"?
The API's three replicas must not all land on one machine, the GPU inference job must land only on GPU nodes, and no ordinary workload may consume the expensive GPU nodes just because they had free CPU.
Automatic, constraint-aware placement: each pod is assigned to a node that satisfies its resource requests and its placement rules, with the reasoning recorded in events when no node qualifies.
Filter, score, bind
The scheduler watches for pods with no node assigned and runs a two-phase decision for each. Filter removes every node that cannot host this pod: not enough *allocatable* CPU or memory left after existing requests, a taint the pod does not tolerate, a node selector or affinity rule that does not match, a required volume that lives in another zone, a host port already taken. Score ranks the survivors — spreading across zones and nodes, preferring nodes that already have the image, balancing resource usage. Then it binds: writes the chosen node onto the pod, and the node agent takes over.
The word doing the work in the filter phase is requests. The scheduler does not look at how much memory a node is currently *using*; it looks at the sum of the requests of the pods already assigned to it. A node running at 20% actual memory can be completely unschedulable because the pods on it requested everything. Conversely, a node can be at 95% real memory pressure and still accept pods, because those pods requested very little. Understanding that one indirection resolves the majority of confusing scheduling behaviour — and it is why Requests vs Limits: Two Numbers That Do Different Jobs is the next lesson.
It is also worth knowing what the scheduler will not do. It does not move running pods to improve packing; placement is decided once, at bind time. If your cluster becomes badly balanced, it stays badly balanced until something restarts the pods. The scheduler is a placement decision, not a continuous optimizer.
Pending is not a mystery — it is a list
A pod stuck in Pending is the most common Kubernetes support question and one of the easiest to answer, because the scheduler writes down exactly which filter eliminated how many nodes. The message reads like a tally: so many nodes failed on insufficient memory, so many on a taint, so many on affinity. Every line names a different fix.
The reflex to resist is deleting the pod. Its replacement will be evaluated by the same scheduler against the same nodes with the same constraints, and will be equally unplaceable — you have simply lost the events that explained why. Read the events first.
The second reflex to resist is assuming a capacity problem. In the example below, three separate causes are stacked: a genuine capacity shortfall, a taint that was doing its job, and a topology spread rule that the cluster could not satisfy. Adding nodes fixes only the first, and if the new nodes carry the same taint it fixes nothing at all.
$ kubectl describe pod api-6f7d
Status: Pending
Events:
Warning FailedScheduling 2m (x9 over 14m) default-scheduler
0/12 nodes are available:
4 Insufficient memory, <-- real capacity: requests already committed
3 node(s) had untolerated taint {gpu=true}, <-- working as intended; do NOT tolerate it
2 node(s) had volume node affinity conflict,<-- the pod's volume is in another zone
2 node(s) didn't match pod topology spread constraints,
1 node(s) were unschedulable. <-- cordoned for maintenance
$ kubectl describe node node-04
Allocatable: cpu: 3800m memory: 14Gi <-- NOT the machine's total; system reserves first
Allocated resources (requests, not usage):
cpu 3650m (96%) memory 13.4Gi (95%)
# Actual memory in use on this node: 4.1Gi.
# The node is 29% used and 95% requested. Requests are what the scheduler sees.Constraints, and the discipline of using few of them
Beyond resources there are four placement tools, and they are best learned in order of how often they are genuinely needed. Topology spread constraints are the one nearly every production workload should use: they say "keep my replicas on different nodes, or in different zones", which is what turns replicas: 3 into actual redundancy. Node selectors pin a workload to a labelled class of machine, which is the simple way to say "this needs GPU nodes".
Taints and tolerations are the inverse and the one people invert in their heads. A taint is on the *node* and repels pods; a toleration is on the *pod* and permits it to be placed there anyway. Crucially, a toleration does not attract — it only removes an objection. So "GPU nodes should only run GPU work" needs both: a taint so ordinary pods stay off, and a node selector so GPU pods actually go there. Using only a toleration produces the exact failure of a batch job scheduled onto expensive accelerators to do nothing.
Affinity and anti-affinity are the most expressive and the most abused. Required (hard) rules make pods unschedulable when they cannot be satisfied, which is usually not what the author intended; preferred (soft) rules degrade gracefully. Pod anti-affinity across a large cluster is also genuinely expensive to evaluate. The honest guidance: use topology spread for redundancy, node selectors plus taints for specialized hardware, and reach for affinity only when you can name the specific placement that must not happen.
| Tool | Lives on | What it does | The common mistake |
|---|---|---|---|
| Resource requests | Pod | Reserves capacity and is the primary filter for every node. | Omitting them, which makes placement arbitrary and eviction likely. |
| Topology spread | Pod | Distributes replicas across nodes or zones so a failure is partial. | Not using it, then discovering all three replicas were on the node that died. |
| Node selector / node affinity | Pod | Restricts placement to nodes with matching labels. | Using a hard requirement where a preference would degrade gracefully. |
| Taint | Node | Repels pods that do not explicitly tolerate it. | Assuming it attracts. It never attracts — it only repels. |
| Toleration | Pod | Removes the objection a taint raises. Grants permission, not preference. | Adding a toleration alone and expecting the pod to land on those nodes. |
| Pod anti-affinity | Pod | Keeps pods away from other pods matching a selector. | A required rule that silently makes replicas unschedulable as the cluster fills. |
Key points
- Scheduling is filter, score, bind: eliminate nodes that cannot host the pod, rank the survivors, write the choice onto the pod.
- The filter uses resource *requests*, not actual usage — a node at 29% real memory can be 95% requested and completely unschedulable.
- Placement is decided once at bind time; the scheduler never moves a running pod to improve balance.
Pendingis explained in the pod events, itemized by which filter eliminated how many nodes. Deleting the pod destroys the explanation and changes nothing.- A taint repels and a toleration permits; neither attracts. Dedicating expensive nodes needs a taint *and* a selector.
The loop, answered
Every field is required, which is why no lesson here can recommend something without saying what it costs and what simpler thing to consider first.
- • The scheduler watches for pods with an empty node assignment.
- • Filter plugins eliminate nodes on allocatable resources, taints, selectors, affinity rules, volume zone affinity and port conflicts.
- • Score plugins rank the remaining nodes on spreading, image locality and resource balance.
- • The winner is written to the pod's node field — the bind — and the decision is final for that pod's lifetime.
- • The node agent on the bound node sees the assignment, pulls images, mounts volumes and starts containers.
- • Setting resource requests on every workload, because a pod with none is placed blindly and evicted first under pressure.
- • Topology spread rules for anything whose replica count is meant to provide redundancy.
- • Node pool design: how many machine types, which are tainted, and who is allowed to tolerate what.
- • Headroom for rescheduling — a cluster with no free capacity cannot heal, because healing is scheduling.
- • Cluster autoscaling if you use it, which adds nodes when pods are Pending and therefore inherits every constraint bug as a cost.
- • Pods Pending because requests exceed anything allocatable — often because someone requested a whole node's worth without noticing system reservations.
- • Pods Pending on a volume zone conflict, which no amount of added capacity resolves.
- • All replicas scheduled onto one node in the absence of a spread rule, converting a single machine failure into a full outage.
- • A required anti-affinity rule that silently caps how many replicas can ever be scheduled, so autoscaling stops working at an arbitrary number.
- • An overly broad toleration on a common workload, which lets ordinary pods consume dedicated GPU or memory-optimized nodes.
- • Scheduling throughput is usually fine; what degrades is decision *quality* on a full cluster where few nodes survive the filter.
- • Pod anti-affinity evaluation is expensive at large node counts and is a known source of scheduling latency.
- • A cluster that autoscales couples scheduling to node provisioning, so a Pending pod now costs minutes of machine startup — see Autoscaling.
- • Taints and node pools are a workload isolation mechanism: untrusted or multi-tenant workloads can be confined to a dedicated pool.
- • That is isolation at the machine level, which is stronger than namespace separation and weaker than a separate cluster.
- • Permission to set tolerations is permission to place workloads onto nodes you meant to reserve, including nodes with elevated access.
- • Scheduling onto a node co-locates a workload with everything else there; for sensitive data the relevant question is who else runs on that machine.
- • Bin-packing quality is the main cost lever in a cluster: honest requests let more pods share fewer machines.
- • Over-stated requests are the most common source of cluster overspend — they reserve capacity nobody uses and nobody can reclaim.
- • Strict placement constraints reduce packing efficiency by design; every hard rule is capacity you are choosing not to use.
- • Dedicated node pools trade utilization for isolation, which is a real cost and often the right one.
- • Pending pod count and age — the leading indicator of a capacity or constraint problem, and the trigger for cluster autoscaling.
- • Requested versus allocatable versus actually-used per node: the gap between the second and third is your right-sizing opportunity.
- • Scheduling latency and failure events, which show constraint problems before they become visible outages.
- • Replica distribution across nodes and zones, which is the only way to verify a spread rule is doing what you think.
- • The signal that lies: node CPU and memory utilization graphs. A node at 30% usage can be 95% requested and unable to accept a single pod.
- • No scheduler at all: on a single machine, everything runs there and placement is not a question.
- • A managed container platform that places tasks for you with a much smaller constraint vocabulary — usually just a machine type and a count.
- • Separate clusters instead of taints and node pools, when isolation matters more than utilization.
- • A serverless platform, where placement is entirely the provider's problem and you never see a Pending state.
- • Buys automatic, constraint-aware placement across a fleet; costs a filter you must be able to read when nothing survives it.
- • Buys high utilization through bin-packing; costs the accuracy of every resource request you wrote.
- • Buys expressive placement rules; costs schedulability — every hard constraint is a way for pods to become unplaceable later.
Why is this pod Pending?
What people believe, and what is true
The scheduler places pods based on how busy each node is.
It uses requests, not usage. A node at 30% actual utilization can be fully requested and reject every pod.
A toleration makes a pod prefer those nodes.
A toleration only removes an objection. To actually target nodes you also need a selector or affinity rule.
A Pending pod means the cluster is out of capacity.
Capacity is one of several filters. Volume zone conflicts, taints and spread constraints produce the same status, and adding nodes fixes none of them.