How Kubernetes Decides Where Pods Run
Cover photo by Winston Chen on Unsplash
Some folks argue that Kubernetes scheduling is just magic. I get why they say that. When you deploy a yaml file, the pod appears on a node. It feels effortless. But there’s a mechanical process happening under the hood that matters when things break.
How it actually works
The scheduler acts as a controller. It watches the API server for pods without a node assignment. Once it finds one, it runs through two phases: Filtering and Scoring. First, it filters out nodes that can’t host the pod. If your pod requests 4GB of RAM and the node only has 2GB free, the node gets tossed out. Simple enough.
Next, the scheduler scores the remaining nodes. It checks things like resource usage and affinity rules. If you’ve ever defined nodeAffinity or podAntiAffinity, this is where the engine earns its keep. It’s essentially a priority queue calculation.
The comparison: Manual vs. Automatic
You might think, “Why wouldn’t I just hardcode nodes to my pods for performance?” That’s a fair question, but it usually ends in tears. If you hardcode nodes, you lose the ability to shift workloads during maintenance or node failure (which happens more than we like to admit).
Compare that to using nodeSelector or standard taints and tolerations. Using these native primitives keeps your cluster resilient.
Here is a simple example of a constraint:
spec: affinity: nodeAffinity: requiredDuringSchedulingIgnoredDuringExecution: nodeSelectorTerms: - matchExpressions: - key: disk-type operator: In values: - ssdWhen my advice falls short
I’ll be honest, if you’re running a massive, high-frequency trading platform or something that requires specific hardware tuning (like direct GPU access or localized SSD arrays), the standard scheduler might not be enough. You might need to look into building a custom scheduler or using complex priority classes. For 99 percent of applications, though, the standard logic is fine.
I often see teams fighting the scheduler by over-engineering their node placement. It’s rarely worth the effort. Let the platform handle the heavy lifting. If you try to outsmart the scheduler, you’re usually just creating technical debt for the next person who has to debug a pending pod.
Keep it simple. Use labels effectively. That’s it.
Are you still running into issues where pods refuse to schedule even when you’re sure there is room?