Skip to main content

Karpenter on Bare Metal Is Not Magic: The Supply Chain Behind a NodeClaim

11 min readDora NodaDora Noda
Share
On this page

Kubernetes can make a very persuasive promise: a Pod cannot fit, so the cluster adds a node. That sentence is almost true on a hyperscaler, where an API can usually turn a request into a VM. On bare metal, it conceals the only question that matters during a burst: what system can turn this NodeClaim into a healthy machine before the workload’s deadline?

vCluster Auto Nodes is an important answer to the integration problem. It places Karpenter’s constraint-aware scheduling behind a virtual cluster and can fulfill requests with Terraform/OpenTofu, KubeVirt, or NVIDIA Base Command Manager. But it does not make a rack of already-owned machines behave like an infinite VM API. It makes the boundary explicit—and that is exactly what a platform team needs to design well.

The answer first: a NodeClaim selects capacity; a provider delivers it

Here is the boundary to keep in mind. vCluster’s Auto Nodes documentation describes Karpenter watching unschedulable Pods, creating a Karpenter NodeClaim, then having vCluster Platform create a platform NodeClaim that a node provider fulfills. Those are deliberately different jobs.

StageWhat it can decideWhat it cannot guarantee
KarpenterWhich allowed node type satisfies resource requests, labels, taints, affinities, topology rules, and pool limitsThat the selected machine exists, can be acquired, or will join in time
vCluster PlatformWhich configured provider and node type should fulfill the claimThat the provider has quota, stock, working credentials, or a valid image
Terraform/OpenTofu, KubeVirt, BCM, or another providerCreate a VM, activate a prepared host, or run the infrastructure workflowThat bootstrap succeeds, the network works, or kubelet becomes Ready
KubernetesSchedule the Pod after the worker is registered and ReadyThat an emergency workload can wait through procurement or a reimage

That distinction applies to every node autoscaler. Kubernetes’ own node-autoscaling overview says autoscalers need an integration with an infrastructure provider to create and delete the resources backing Nodes. Karpenter is better thought of as a highly capable capacity-selection engine, not as a source of capacity.

Follow three pending workloads through a real inventory

Make the discussion reproducible. Imagine a tenant cluster with this ready inventory; all quantities are allocatable capacity available to new Pods, not the machines’ total specifications.

Ready workerFree capacity and traits
general-a8 vCPU, 28 GiB memory
general-b8 vCPU, 28 GiB memory
gpu-a16 vCPU, 120 GiB memory, one free A100; labelled accelerator=nvidia-a100

Now three demands arrive together:

| Workload | Pod constraints | Outcome before any new machine | If it needs a NodeClaim | | --- | --- | --- | | Web burst | six Pods, each 1 vCPU/2 GiB | Fits on the two general workers; no NodeClaim is necessary | Nothing should be provisioned. Correct requests let the scheduler use capacity already paid for. | | Batch job | one Pod, 20 vCPU/40 GiB | Cannot fit on a single current worker | Karpenter can select an allowed 32-vCPU general node type and emit a NodeClaim. | | GPU inference | two Pods, each 8 vCPU/32 GiB and accelerator=nvidia-a100 | The first fits on gpu-a; the second cannot share its only free GPU | Karpenter can request a node type offering an A100, provided the NodePool permits it. |

The NodeClaim is a valuable improvement over a simple “add one more node from pool X” response. It incorporates the actual shape of the unschedulable Pod and excludes types that cannot satisfy it. vCluster documents that it passes fitting, cost-sorted node types and requested resources to the platform, which then selects a type and invokes its configured provider. That is useful bin packing.

But each claim takes one of four very different physical paths:

Supply state for the selected typeBatch-job resultSecond GPU-job resultIllustrative time budget
Matching worker is already ReadyIt should have scheduled without a claim; investigate stale capacity/request accountingSameScheduler latency, not provisioning time
Warm, pre-prepared worker or an immediately available VMProvider attaches or creates it, bootstrap joins it, readiness gate passesSame, if a warm A100 host existsUsually minutes; test the actual image, network, and join path
Bare-metal spare needs PXE boot or reimageClaim may be fulfilled, but it is a slow burst pathSame, plus GPU-driver validationCommonly tens of minutes in a well-run operation; measure your own path
No matching spare, provider quota, or vendor stockPod stays pending despite a valid NodeClaimPod stays pendingDays or weeks if acquisition/delivery is required—outside an autoscaler’s control

Those are operational bands, not Karpenter service-level guarantees. The important consequence is practical: a 30-second web spike cannot be rescued by a 30-minute bare-metal reimage. A warm reserve, a virtualisation layer, or a capacity buffer handles that spike; Karpenter simply makes the request for the next worker more precise.

The sensitivity test: when bin packing changes the outcome

Before enabling dynamic provisioning, run the same workload table against the variables that change its outcome. This prevents a demo where an autoscaler looks impressive because the difficult part—supply—was quietly pre-arranged.

VariableWhat changes in the exampleOperational implication
Warm matching inventoryA prepared 32-vCPU host makes the batch claim practical; a prepared A100 host makes the second GPU claim practicalKeep a measured reserve for the workload’s recovery objective, or accept pending Pods.
Request-to-Ready timeA five-minute VM workflow may be fine for an asynchronous job, while a 30-minute physical workflow is notSet workload deadlines and capacity policy together; do not call every delayed path “elastic.”
Provider quota or stockA NodeClaim can select a node type that the provider cannot issueMonitor quota and inventory as production dependencies; a controller cannot bargain for unavailable hardware.
Resource-request accuracyUnderstated requests may pack Pods onto a node that later throttles or OOMs; overstated requests create unnecessary claimsAutoscaling is only as truthful as the requests it models. Profile workloads and retain headroom for DaemonSets.
Consolidation budgetAggressive consolidation may evict a node that took a long time to prepare; restrictive budgets retain expensive idle capacityUse disruption budgets and consolidateAfter as availability policy, not merely as a cost switch.

vCluster exposes Karpenter-style disruption configuration, including consolidation policy, delay, and budgets. The default it documents can consolidate empty or underutilized nodes after 30 seconds. That default is a sensible cloud starting point, but it deserves deliberate review on bare metal: a node that took half an hour to reimage should not necessarily be treated like a disposable VM.

What the fulfillment contract needs beneath Karpenter

“Karpenter on bare metal” becomes honest only when every row below has an owner and a tested failure mode.

Required capabilityWhy it existsAuto Nodes mapping
A provider adapter with scoped credentialsSomething must create, activate, or release the machineA configured vCluster node provider; supported paths include Terraform, KubeVirt, and NVIDIA BCM.
A typed hardware catalogKarpenter needs CPU, memory, GPU, zone, and custom properties to rule types in or outNode types become claim requirements such as instance type or region.
Capacity sourceSelection is pointless if nothing can satisfy itCloud API capacity, virtualised hosts, or physical hosts already prepared for activation.
Immutable bootstrap and joinA created server is not yet a workerImage/user data install the runtime and join with short-lived credentials.
Serialized, idempotent infrastructure stateTwo claims must not race to claim the same machineTerraform/OpenTofu state needs a locking backend; Terraform’s documentation explains that locking prevents concurrent writers from corrupting state.
Readiness and health gatesKubelet Ready alone may be too early for CNI, storage, or GPU driversMark the claim successful only when the required node services are actually ready.
Safe deprovisioningConsolidation must not turn into data loss or a surprise outageCordon, drain within disruption budgets, release the machine, and reconcile stranded resources.

The chain is therefore: unschedulable Pod → Karpenter NodeClaim → platform NodeClaim → provider action → bootstrap → joined Node → readiness gate → scheduled Pod. Instrument each arrow separately. “Node provisioning is slow” is not a usable incident diagnosis; “state lock held,” “PXE failed,” “GPU driver gate failed,” and “provider quota exhausted” are.

Cluster Autoscaler remains the simpler baseline for many CAPI fleets

Karpenter is not the only valid response to pending Pods. Kubernetes distinguishes its model from Cluster Autoscaler: Cluster Autoscaler grows and shrinks preconfigured node groups; Karpenter uses operator-provided constraints to auto-provision suitable nodes. Both can consolidate nodes, and both still require an infrastructure integration.

QuestionCluster Autoscaler with Cluster APIKarpenter through vCluster Auto Nodes
Capacity definitionOperator declares homogeneous MachineDeployment/node groups and min/max sizesOperator declares allowed node types, requirements, limits, and disruption policy in NodePools
Scale actionAdjusts the size of an existing groupSelects a fitting type and creates a NodeClaim
Best operational shapeA small, stable menu of worker pools with clear reservationsDiverse workload shapes where the provider can fulfill constrained choices quickly
Bare-metal dependencyMachine deployment still needs an automatable provider and available hostsNodeClaim still needs an automatable provider and available hosts
Cluster API integrationUses Cluster Autoscaler’s Cluster API provider to manage worker MachinesIs not a replacement for CAPI’s lifecycle/health controls; it adds tenant-scoped selection and fulfillment machinery

The Cluster API Book’s autoscaling guide documents the Cluster Autoscaler provider as managing worker-machine provisioning and deprovisioning through Cluster API. That is an attractive, boring default when a fleet has, for example, general and GPU MachineDeployments with well-understood minimums and maximums.

The key difference is not “old autoscaler versus new autoscaler.” It is whether the variability Karpenter can model—different hardware traits, zones, and workload requirements—outweighs the extra provider and fulfillment system required to execute the choice.

The Hetzner boundary: Cloud API capacity and physical stock are different products

A Hetzner-backed CAPI fleet needs to name which kind of Hetzner capacity it is using. Hetzner Cloud offers an API-facing VM capacity interface. For stable worker shapes and predictable demand, Cluster Autoscaler’s named-pool model keeps that interface simple: CAPI reconciles the chosen MachineDeployment, while the autoscaler changes its desired size.

Auto Nodes becomes materially different when virtual clusters need to choose among a broad, fast-to-fulfill catalog—say, general CPU, high-memory, and GPU types—with constraints that would otherwise cause an explosion of static pools. The provider must still be able to honor those choices quickly enough for the workload. That is a supply-chain condition, not a Karpenter feature.

Dedicated servers and true bare-metal inventory have a harder boundary. If the operation has warm, matching hosts or a virtualisation layer, Auto Nodes can make that reserve available to the right tenant workload without manually choosing a pool. If it does not, a valid NodeClaim merely records an unmet need. A planned fixed pool plus a consciously sized reserve is the truthful capacity policy until the physical activation path meets the workload’s tolerance.

That is not a limitation to hide. It is the design decision a self-hosted platform gets to own: choose where the buffer lives, expose the relevant inventory and readiness state, and let automation act only inside those boundaries.

Make the promise measurable

The useful promise of Karpenter-enabled bare-metal autoscaling is not “hardware appears on demand.” It is: the platform can select the right capacity, invoke an owned fulfillment path, and tell you exactly where that path failed.

For a platform such as Bex, that separation is healthy. A Git-push PaaS can keep ordinary applications on predictable worker pools while exposing enough machine and workload state for an operator—or an AI agent—to identify whether a pending deployment needs a new node, a quota change, a warmed spare, or a different workload request. The system becomes more autonomous without pretending the physical world is an API call.

Bex.co is the open-source, AI-native Render alternative: push a Git repo and get a running HTTPS service on machines you own. Its Cluster API-based approach is a natural place to keep machine lifecycle and application intent explicit rather than hiding capacity limits behind a hosted-platform abstraction.

Sources

Related articles

Run this on infrastructure you own

bex is the open-source, AI-native Render alternative — push a git repo and get a running HTTPS service on your own machines.

Get started with bex