A pending pod on EKS with Karpenter gets a right-sized node in 30 to 60 seconds. The same pending pod on a Cluster Autoscaler fleet waits 2 to 5 minutes for a node that was picked months ago from a fixed menu of instance types. That gap — seconds versus minutes, best-fit versus fixed-menu — is why every team running Kubernetes on machines they own has Karpenter envy. And in September 2025, vCluster seemed to answer it: Auto Nodes, "Karpenter-based dynamic autoscaling" for public cloud, private cloud, and bare metal.
Here is the honest version: Karpenter still doesn't run on bare metal in 2026. Auto Nodes doesn't change that. It wraps Karpenter's brain in a Terraform body — Karpenter decides the node shape, Terraform or OpenTofu provisions it — which means the provisioning path inherits exactly the delays and fixed-shape compromises Karpenter was built to escape. For a Cluster-API-managed fleet on Hetzner or bare metal, the mature default is still Cluster Autoscaler. This post totals that gap stage by stage so you can see precisely what the envy costs and what to run instead.
Why there is no bare-metal Karpenter provider
Karpenter's architecture has exactly one seam where infrastructure plugs in: the cloud provider. Karpenter core watches for unschedulable pods, computes the cheapest fitting capacity, and calls a provider to materialize it. On AWS that call is a direct EC2 Fleet API request — no Auto Scaling group in the middle, which is where most of the 2-to-5-minute Cluster Autoscaler delay lives. The provider model assumes an elastic cloud API that can conjure right-sized capacity on demand.
Bare metal has no such API. There is no "RunInstances" for a rack of servers you own; there is a provisioning system, an OS install, a boot sequence, and a kubelet join, each owned by you. A Karpenter provider for owned hardware would have to be a full machine-lifecycle manager, which is a different product wearing a provider costume. That is why, after years of multi-cloud expansion, the provider landscape looks like this:
| Provider | Status in 2026 | Notes |
|---|---|---|
AWS (karpenter-provider-aws) | Stable, mature | Powers EKS Auto Mode; the reference implementation |
Azure (karpenter-provider-azure) | GA | Backs AKS node autoprovisioning (NAP) |
GCP (cloudpilot-ai/karpenter-provider-gcp) | Community, pre-stable | Not Google-official; open questions about donation to Kubernetes/CNCF; early adopters listed |
| AlibabaCloud, Bizfly Cloud | Community | Narrow, vendor-specific efforts |
Cluster API (kubernetes-sigs/karpenter-provider-cluster-api) | Experimental (~v0.2.0) | Basic create/delete only; known blocking-create issues under burst |
| Bare metal / Hetzner / any owned hardware | Nonexistent | No native provider; would need a machine-lifecycle manager |
The furthest the ecosystem has reached toward owned hardware is the experimental CAPI provider — and look at what it does under the hood: it picks a compatible MachineDeployment and bumps its replica count. The just-in-time magic collapses back into the same "scale a predefined group" motion Cluster Autoscaler performs. Keep that mechanism in mind; it is the whole story.
The numbers behind the envy
Before dismissing the wrappers, credit what teams are chasing. The 2026 comparisons (ScaleOps' EKS guide, community ADRs, AWS's own writeup of Salesforce migrating 1,000 EKS clusters from Cluster Autoscaler to Karpenter) converge on the same shape:
| Dimension | Cluster Autoscaler | Karpenter |
|---|---|---|
| Scale-up latency | 2–5 minutes (via ASG/MNG indirection; 3–4 min typical on EKS) | 30–60 seconds (direct cloud API call) |
| Scaling trigger | Time-driven: ~10s scan loop over pending pods | Event-driven: reacts to unschedulable pods immediately |
| Node selection | Fixed menu: picks among predefined node groups | Just-in-time: computes cheapest instance type for the actual pending pods |
| Bin-packing | Limited: bound to group shapes; prone to stranded capacity | Aggressive: consolidates, including emptiness-first replacement of underused nodes |
| Scale-down | Reactive, 10+ minutes to drain and terminate | Proactive consolidation, often under a minute |
Salesforce's migration is the strongest public data point: scaling latency fell from minutes to seconds across a thousand-cluster fleet, and utilization improved because Karpenter's bin-packing left fewer stranded resources. That is a real result on elastic cloud APIs. The question is how much of it survives the trip to hardware you own.
What vCluster Auto Nodes actually is
Announced September 9, 2025, vCluster Auto Nodes brings "Karpenter-based dynamic autoscaling to all environments: public cloud, private cloud, and bare metal," in CEO Lukas Gentele's words. The mechanism matters more than the slogan. Auto Nodes splits autoscaling into two halves:
- Decision (Karpenter): Karpenter core watches pending pods and decides what capacity should exist, exactly as on EKS.
- Provisioning (Terraform/OpenTofu): instead of a cloud-provider API call, the decision fans out to a Terraform/OpenTofu provider definition — plus native integrations for NVIDIA Base Command Manager and KubeVirt for GPU-heavy bare-metal pools.
This is genuinely useful plumbing: one decision engine, many provisioning backends, declarative infra definitions teams already have. But notice what crossed the seam unchanged: every delay and rigidity of the provisioning backend. Karpenter can decide in milliseconds that it wants a 4-vCPU node; if the Terraform path then applies a plan, boots a server, runs cloud-init, and joins the kubelet, the pod still waits for all of that. The brain got faster. The body didn't.
The catch, totaled: an end-to-end scale-up budget
This is the table the marketing skips. A scale-up is a pipeline, and the total is what the pending pod feels. Approximate but representative stage budgets:
| Stage | EKS + Karpenter | Hetzner + CAPI + Cluster Autoscaler |
|---|---|---|
| Notice the pending pod | ~instant (event-driven) | 0–10s+ (scan loop over pending pods) |
| Decide capacity | milliseconds (cheapest fit computed) | milliseconds (pick least-wasteful MachineDeployment) |
| Order the machine | one EC2 Fleet API call, ~seconds | MD replica bump → CAPI reconcile → Hetzner API: tens of seconds |
| Boot + configure | optimized AMI, kubelet joins: ~30s | Server boot + cloud-init + kubeadm join: 1–3 min |
| Pod schedules | image pull, then running | image pull, then running |
| Typical total | 30–60 seconds | 3–7 minutes |
Two sensitivities decide where in the range you land. First, image and cloud-init weight: a pre-baked snapshot with the kubelet pre-installed and a lean cloud-init lands near the fast end; a bootstrap that installs packages at boot lands near the slow end on every provider, cloud included. Second, burst shape: a single scale-up is one pipeline; a burst of fifty pending pods behind one MachineDeployment serializes through CAPI reconciliation and the cloud account's own rate limits, while Karpenter's direct calls parallelize better. The delta is real, roughly 2–6 minutes per scale-up event — and it lives almost entirely in stages Karpenter's scheduler never touches.
The bin-packing half of the envy deserves the same honest totaling. Take a concrete, typical case: a pending pod needs 1.5 vCPU, and your fleet's MachineDeployments offer 2-vCPU and 8-vCPU shapes. Cluster Autoscaler can only grow a group it has — often the 8-vCPU one, stranding 6.5 vCPU until other pods fill it. Karpenter on a cloud API would order exactly a 2-vCPU instance, stranding 0.5. That is the mechanism, and on elastic APIs it compounds across hundreds of decisions a day into the utilization wins Salesforce reported. On owned hardware, the menu is your servers: Karpenter-via-Terraform still picks from machine shapes you already own or images you pre-built. The decision is smarter, but the waste floor is set by your hardware, not the scheduler. Expect better packing of heterogeneous pods onto fixed nodes — real, but measured in percent, not the step-change cloud teams report.
What a CAPI fleet should actually run
Given all of that, the recommendation is boring, and boring is the point. Run Cluster Autoscaler with its native clusterapi provider. It watches for pending pods, consults the capacity annotations on your MachineDeployments, and bumps replicas; CAPI and CAPH do the rest. The entire wiring is two annotations per MachineDeployment:
metadata:
annotations:
cluster.k8s.io/cluster-api-autoscaler-node-group-min-size: "1"
cluster.k8s.io/cluster-api-autoscaler-node-group-max-size: "10"Plus the capacity.cluster-autoscaler.kubernetes.io/* annotations (cpu, memory, maxPods) so the autoscaler can simulate scheduling without trial nodes. This path is a decade old, provider-agnostic, and debuggable with kubectl describe machinedeployment. It fails in ways the community has already documented instead of ways you get to discover.
And here is the symmetry worth naming outright: if you later bolt Karpenter onto CAPI — via the experimental provider or a Terraform wrapper — your autoscaling still has more in common with Cluster Autoscaler's periodic-reconciliation model than with Karpenter's event-driven one. Both reconcile desired-versus-actual machine counts through MachineDeployments on a loop; neither conjures right-sized capacity from an elastic API. The scheduler got fancier. The control loop is the same shape. Choose the mature implementation of that loop until the experimental one earns it: the CAPI provider today is basic create/delete with known burst-path issues, which is exactly the workload (a burst of pending pods) you buy an autoscaler for.
When this advice expires
Three things would change the answer. First, the karpenter-provider-cluster-api project maturing past experimental — e2e-tested, burst-safe, consolidation-aware against MachineDeployments — would make Karpenter-on-CAPI a real choice rather than a demo. Second, a genuine machine-lifecycle API for owned hardware (something like a buyable "RunInstances for your rack," whether via BCM-style managers or KubeVirt pools with warm capacity) would move the provisioning stages, not just the decision stage. Third, GPU-heavy fleets may feel the pain first: Dynamic Resource Allocation going stable means device-aware scheduling pressure is only growing, and whoever wires DRA to owned accelerators best wins the next round of envy. Until then, take the boring default, pre-bake your images, keep cloud-init lean, and spend the saved engineering weeks on your tenants — not on re-skinning a control loop you already own.
Bex.co is the open-source, AI-native Render alternative — push a git repo, get a running HTTPS service on machines you own. Star the repo on GitHub or deploy your first app today.



