Kubernetes traditionally treats node readiness as a small set of central conditions. That binary view is not enough for modern clusters where a node may be reachable but not actually usable for every workload. Network plugins, storage drivers, GPU firmware, device managers, image pre-pulls, and custom node-local services can all lag behind the kubelet becoming visible to the control plane.
A node readiness controller adds a declarative scheduling gate in front of those dependencies. Operators define rules that map custom node conditions to taints. The controller applies a NoSchedule taint while the required conditions are missing and removes it when the node satisfies the platform-specific readiness contract.
The result is a safer bootstrapping path: the scheduler sees the node only after the surrounding infrastructure is ready, and operators can choose whether a rule is a one-time bootstrap gate or a continuous lifecycle guard. This is especially useful for heterogeneous clusters where worker, GPU, storage-heavy, and specialized nodes each need different readiness definitions.
The controller does not need to run every health check itself. It reacts to node conditions reported by kubelet integrations, Node Problem Detector, custom agents, or lightweight condition reporters. This keeps the scheduling policy decoupled from the code that measures local readiness.
A taint is the enforcement mechanism. While a readiness condition is absent or not in the required state, the controller keeps a taint on matching nodes. Pods without a matching toleration cannot be scheduled there, which prevents half-ready infrastructure from receiving normal workloads.
Some checks matter only during initialization, such as CNI startup or image pre-pulling. Others matter for the full node lifecycle, such as a GPU driver or storage agent that must stay healthy. A readiness rule should encode that distinction so the controller either stops after successful bootstrap or keeps reconciling.
The kubelet’s basic Ready condition says the node participates in the cluster. A node readiness controller lets platform teams define what ready means for their workloads, hardware, and dependencies before the scheduler places pods.
A rule selects nodes, declares required conditions, defines the taint to manage, and chooses an enforcement mode. This example keeps worker nodes unschedulable until a custom network condition reports true.
apiVersion: readiness.node.x-k8s.io/v1alpha1
kind: NodeReadinessRule
metadata:
name: network-readiness-rule
spec:
nodeSelector:
matchLabels:
node-role.kubernetes.io/worker: ""
conditions:
- type: "cniplugin.example.net/NetworkReady"
requiredStatus: "True"
taint:
key: "readiness.example.com/network-unavailable"
value: "pending"
effect: "NoSchedule"
enforcementMode: "bootstrap-only"
The controller watches rules and nodes, evaluates the selected nodes against the required conditions, and patches taints to match the desired scheduling state. Status fields record applied nodes, failed nodes, detailed evaluations, and dry-run summaries so operators can audit the effect before enforcing a rule.
func reconcileRule(ctx context.Context, rule NodeReadinessRule) error {
nodes := listNodes(rule.Spec.NodeSelector)
for _, node := range nodes {
ready := allConditionsMatch(node.Status.Conditions, rule.Spec.Conditions)
hasTaint := containsTaint(node.Spec.Taints, rule.Spec.Taint)
switch {
case !ready && !hasTaint:
patchAddTaint(ctx, node.Name, rule.Spec.Taint)
case ready && hasTaint:
patchRemoveTaint(ctx, node.Name, rule.Spec.Taint)
}
recordEvaluation(rule, node, ready)
}
return updateRuleStatus(ctx, rule)
}
A node can register before its networking layer is ready. A readiness rule can keep the node tainted until the CNI agent or service-mesh dataplane reports healthy, preventing pods from landing on a node that cannot provide expected connectivity.
Accelerator nodes often require drivers, firmware, device plugins, and partitioning steps before they are usable. A continuous rule can remove the scheduling gate only after those components report healthy and reapply it if the dependency fails later.
Storage-heavy clusters may need local volumes, CSI drivers, or mount helpers before pods start. Readiness rules prevent workloads from being scheduled into an initialization race that would otherwise become pod churn or repeated mount failures.
A readiness rule can affect large parts of a cluster. Dry-run mode lets operators evaluate which nodes would be tainted, which taints would be removed, and which conditions are missing before the controller mutates live scheduling state.
apiVersion: readiness.node.x-k8s.io/v1alpha1
kind: NodeReadinessRule
metadata:
name: gpu-driver-readiness
spec:
dryRun: true
nodeSelector:
matchLabels:
accelerator: nvidia
conditions:
- type: "gpu.example.com/DriverReady"
requiredStatus: "True"
taint:
key: "readiness.example.com/gpu-driver-not-ready"
effect: "NoSchedule"
enforcementMode: "continuous"
Keep health detection close to the node and policy in the controller. A DaemonSet can check local HTTP endpoints, device files, or agent status and patch a custom node condition. The readiness controller then consumes that condition consistently for scheduling.