Kubernetes Resource Fit Scheduling
Kubernetes Resource Fit Scheduling ensures pods are placed on nodes with sufficient resources, optimizing cluster efficiency and preventing overcommitment.
Kubernetes Resource Fit Scheduling is the specific subset of scheduler candidate evaluation concerned with comparing a Pod's declared CPU, memory, and extended resource requests against each node's remaining allocatable capacity, determining whether a node has enough unclaimed resources to host the Pod at all. It is the single most common reason a Pod remains unschedulable in practice, and understanding exactly how "fit" is computed — what counts toward a node's allocatable capacity, what counts toward a Pod's requirement, and how the two are compared — is essential to correctly sizing workloads and diagnosing capacity-related scheduling failures.
Resource fit is evaluated using resource requests, not limits — a distinction that surprises newcomers, since limits are what actually bounds a container's runtime consumption, while requests are what the scheduler uses to reserve capacity ahead of time.
The Fit Calculation
Node Allocatable Capacity
Each node reports an allocatable capacity for CPU, memory, ephemeral storage, and any extended resources, computed as the node's total physical capacity minus reservations for the operating system and Kubernetes system daemons (kube-reserved, system-reserved, and eviction thresholds).
Summing Existing Requests
For a node to be a feasible candidate, the sum of resource requests from every Pod already scheduled there, plus the requesting Pod's own requests, must not exceed that node's allocatable capacity for each resource type independently — CPU and memory are evaluated as separate constraints, and a node can fail on either one alone.
A node is filtered out of the candidate set the moment this inequality fails for any single resource type, regardless of how much headroom exists for every other resource.
Requests vs. Limits in Fit Calculation
Only Requests Count Toward Scheduling
resources.limits values play no role in the scheduler's fit calculation at all — a container can have a limit far higher than its request (allowing it to burst, subject to actual node capacity at runtime), and the scheduler only ever reserves capacity based on the request, not the limit.
resources:
requests:
cpu: "250m"
memory: "256Mi"
limits:
cpu: "1"
memory: "1Gi"
Overcommitment as a Consequence
Because scheduling only accounts for requests, a node can end up hosting Pods whose combined limits exceed its physical capacity, relying on the assumption that not every Pod will simultaneously burst to its full limit at once — this is a deliberate overcommitment strategy, and one that can, under correlated load spikes, lead to node-level resource contention and kubelet-triggered eviction even though the scheduler's own fit check passed cleanly at placement time.
Pods Without Requests
Implicit BestEffort Behavior
A container with no resource requests specified contributes zero toward the fit calculation from the scheduler's perspective, meaning it can be scheduled onto virtually any node regardless of actual remaining capacity — this Pod is placed in the BestEffort QoS class and is the first to be evicted under node memory pressure, since the scheduler never reserved anything on its behalf in the first place.
Why Explicit Requests Matter
Omitting requests entirely is rarely a deliberate choice for production workloads, since it removes the scheduler's ability to make informed placement decisions and shifts all resource contention risk to runtime eviction rather than proactive placement — setting at least a conservative request, even for a lightweight workload, is standard practice specifically to give the fit calculation something meaningful to work with.
Diagnosing Fit Failures
kubectl describe nodes | grep -A 5 "Allocated resources"
kubectl top nodes
kubectl describe pod codartium-app | grep -A 3 "FailedScheduling"
Comparing a node's already-allocated resource total (from kubectl describe nodes) against its allocatable capacity reveals exactly how much headroom remains for new Pods, which is the direct, quantitative counterpart to the qualitative summary given in a Pod's FailedScheduling event.
Example
apiVersion: v1
kind: Pod
metadata:
name: codartium-fit-example
spec:
containers:
- name: app
image: codartium/app:latest
resources:
requests:
cpu: "500m"
memory: "512Mi"
limits:
cpu: "1"
memory: "1Gi"