Kubernetes Scheduler Plugin Flow
Kubernetes Scheduler Plugin Flow explains how plugins extend the scheduler's decision-making process to manage pod placements across nodes in a Kubernetes cluster.
Kubernetes Scheduler Plugin Flow is the internal architecture by which the kube-scheduler's pluggable framework routes a single Pod through an ordered sequence of independently implemented plugins, each of which can implement one or more extension-point interfaces, collectively producing the scheduler's final decision without requiring any single monolithic piece of code to handle every concern. Rather than a fixed, hardcoded algorithm, the modern kube-scheduler is built entirely around this plugin framework — even the "default" behaviors (resource fit checks, affinity evaluation, taint tolerance) are themselves implemented as in-tree plugins registered against the framework's extension points, the same mechanism available to custom, out-of-tree plugins.
Understanding plugin flow means understanding both the ordered sequence of extension points a Pod passes through, and the fact that multiple plugins can be registered at the same extension point, each contributing independently to that stage's outcome.
The Scheduling Framework's Extension Points, in Order
Sort
Determines the order in which pending Pods are popped from the scheduling queue for processing — typically by priority and creation time, though a custom Sort plugin could implement different tie-breaking logic entirely.
PreFilter and Filter
PreFilter plugins precompute shared state once per scheduling cycle (rather than redundantly per node), and Filter plugins then run per candidate node, each independently able to reject a node from the candidate set — a node is eliminated if any registered Filter plugin rejects it, making this stage a logical AND across all active filter plugins.
PostFilter
Runs only when no node passed filtering, providing the extension point where preemption logic lives — a PostFilter plugin can attempt to make room by nominating eviction victims, potentially rescuing an otherwise completely unschedulable Pod.
PreScore and Score
PreScore plugins prepare shared scoring context, and Score plugins run per feasible node, each producing an independent numeric contribution combined into a weighted sum as described by that node's total scheduling score.
Reserve and Unreserve
Reserve plugins tentatively claim resources on the chosen node before binding actually completes, guarding against a race where a second Pod's scheduling cycle might otherwise double-count the same available capacity; Unreserve runs to release that claim if a later stage fails, keeping internal scheduler state consistent even when scheduling ultimately does not succeed for that Pod.
Permit
Can approve, deny, or delay a Pod's binding — the extension point specifically designed for coordination logic needing to wait on external conditions, such as gang scheduling holding several related Pods until all of them have a viable placement before committing any single one.
PreBind, Bind, and PostBind
PreBind plugins perform any final setup required before binding, Bind plugins perform the actual API write assigning the Pod to its node, and PostBind plugins run purely informational logic afterward with no ability to affect the already-completed outcome.
Multiple Plugins at the Same Extension Point
Independent, Composable Contributions
Because several plugins can register at the same extension point simultaneously, the overall behavior at each stage is a composition of independent, narrowly scoped logic — NodeResourcesFit, NodeAffinity, TaintToleration, and PodTopologySpread might all register as Filter plugins concurrently, each responsible for its own specific hard constraint, with the framework itself simply aggregating their pass/fail verdicts.
Ordering Considerations
Within a given extension point, plugins are generally evaluated in a framework-managed order, typically prioritizing cheaper checks before more expensive ones where the framework can determine this — although the specific ordering guarantee is an implementation detail rather than something workload authors need to reason about directly, since the logical outcome (AND across filters, weighted sum across scores) does not depend on evaluation order.
In-Tree vs. Out-of-Tree Plugins
In-Tree Plugins
The scheduler ships with a comprehensive default set of plugins covering ordinary resource, affinity, taint, and topology-based scheduling needs — these are compiled directly into the scheduler binary and enabled by default in the standard configuration.
Out-of-Tree Plugins
Organizations with specialized scheduling requirements not covered by in-tree plugins can implement and compile custom plugins against the same framework interfaces, then register them within a custom scheduler binary (or, in some setups, load them into a customized build of kube-scheduler itself) — this is the mechanism enabling advanced use cases like gang scheduling, GPU topology-aware placement, or integration with external capacity managers, without needing to fork or reimplement the entire scheduler.
apiVersion: kubescheduler.config.k8s.io/v1
kind: KubeSchedulerConfiguration
profiles:
- schedulerName: codartium-custom-scheduler
plugins:
filter:
enabled:
- name: CodartiumGangScheduling
Example
apiVersion: v1
kind: Pod
metadata:
name: codartium-plugin-flow-example
spec:
schedulerName: default-scheduler
containers:
- name: app
image: codartium/app:latest
resources:
requests:
cpu: "250m"
memory: "256Mi"
kubectl get events --field-selector involvedObject.name=codartium-plugin-flow-example