Scheduling Basics
This page is a hands-on intro to how the kube-scheduler places Pods: the two-phase filter-then-score model, the fields that steer it, and the commands to observe it.
Search across all documentation pages
This page is a hands-on intro to how the kube-scheduler places Pods: the two-phase filter-then-score model, the fields that steer it, and the commands to observe it.
kubectl configured.kind or minikube cluster works for the read-only examples.requests and limits.# Confirm you can see nodes and their capacity
kubectl get nodes -o wide
kubectl describe node <node-name> | grep -A6 AllocatableEvery scheduled Pod carries the node the scheduler chose.
kubectl get pod my-app -o wide
kubectl get pod my-app -o jsonpath='{.spec.nodeName}{"\n"}'spec.nodeName is written by the scheduler at bind time.nodeName plus a Pending status means scheduling has not succeeded yet.-o wide also shows the node's IP, useful for correlating with node problems.nodeName after binding.When no node is feasible, the reason is in the Pod events.
kubectl describe pod my-app | sed -n '/Events/,$p'FailedScheduling with a per-node breakdown like Insufficient cpu or node(s) had untolerated taint.0/5 nodes are available tells you the total node count considered.Requests are the numbers the scheduler uses to check node capacity.
apiVersion: v1
kind: Pod
metadata:
name: my-app
spec:
containers:
- name: app
image: ghcr.io/acme/app:1.4.2
resources:
requests:
cpu: "250m"
memory: "256Mi"
limits:
cpu: "500m"
memory: "256Mi"requests affect the scheduling fit; limits govern runtime throttling and OOM behavior.Guaranteed QoS class.The simplest steering mechanism matches node labels.
apiVersion: v1
kind: Pod
metadata:
name: gpu-job
spec:
nodeSelector:
kubernetes.io/os: linux
node.acme.io/pool: gpu
containers:
- name: trainer
image: ghcr.io/acme/trainer:0.9.0nodeSelector is a hard requirement evaluated during filtering.kubectl label node <name> node.acme.io/pool=gpu.Steering only works if you know the labels available.
kubectl get nodes --show-labels
kubectl get nodes -L topology.kubernetes.io/zone,kubernetes.io/archtopology.kubernetes.io/zone and kubernetes.io/arch are set automatically.-L prints selected labels as columns for quick scanning.You can bypass the scheduler for debugging.
apiVersion: v1
kind: Pod
metadata:
name: debug-here
spec:
nodeName: worker-3
containers:
- name: shell
image: busybox:1.37
command: ["sleep", "3600"]spec.nodeName skips filtering and scoring entirely.Events show scheduling decisions as they happen.
kubectl get events --field-selector reason=Scheduled --watchScheduled event names the Pod and the node it was bound to.FailedScheduling events indicate no feasible node was found.Scheduled with the kubelet Pulling and Started events to trace startup.Node affinity is the expressive successor to nodeSelector.
apiVersion: v1
kind: Pod
metadata:
name: web
spec:
affinity:
nodeAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
nodeSelectorTerms:
- matchExpressions:
- key: kubernetes.io/arch
operator: In
values: ["amd64"]
preferredDuringSchedulingIgnoredDuringExecution:
- weight: 80
preference:
matchExpressions:
- key: node.acme.io/pool
operator: In
values: ["general"]
containers:
- name: web
image: ghcr.io/acme/web:2.1.0required... is a hard filter; preferred... adds score without blocking.IgnoredDuringExecution means the rule is not re-checked after the Pod is running.weight (1 to 100) ranks preferred terms against each other.In, NotIn, Exists, DoesNotExist, Gt, and Lt.Taints repel Pods unless the Pod tolerates the taint.
apiVersion: v1
kind: Pod
metadata:
name: gpu-consumer
spec:
tolerations:
- key: "nvidia.com/gpu"
operator: "Exists"
effect: "NoSchedule"
containers:
- name: infer
image: ghcr.io/acme/infer:1.0.0NoSchedule taint makes the node infeasible.NoSchedule, PreferNoSchedule, and NoExecute.Even spread reduces the blast radius of a zone failure.
spec:
topologySpreadConstraints:
- maxSkew: 1
topologyKey: topology.kubernetes.io/zone
whenUnsatisfiable: DoNotSchedule
labelSelector:
matchLabels:
app: webmaxSkew caps the imbalance in matching Pods between any two zones.whenUnsatisfiable: DoNotSchedule makes it a hard rule; ScheduleAnyway makes it a soft preference.labelSelector defines which Pods count toward the spread.Stack versions: This page was written for Kubernetes 1.36.2, Docker Engine 29.6.1 (BuildKit default), containerd (CRI runtime on nodes), Helm 3, Compose v2, Argo CD (latest - verify at build), and Gateway API (GA - verify controller support at build).
Reviewed by Chris St. John·Last updated Jul 16, 2026