For years, the deal with Cluster API was simple and expensive: every change to a machine meant replacing the machine. Upgrade Kubernetes by one minor version? Replace every node. Rotate a credential baked into the bootstrap config? Replace every node. And because kubeadm-based upgrades walk one minor version at a time, a cluster three minors behind meant repeating the whole replace-and-drain ceremony three times, with an operator babysitting each hop.
Cluster API v1.12, released in late January 2026, breaks both halves of that deal at once. In-place updates let CAPI patch an existing machine — through a new extension mechanism — instead of deleting and recreating it. Chained upgrades let you declare a target version three minors away and have CAPI compute and execute the intermediate steps itself. Here is what that actually changes for a fleet built on the Hetzner provider (CAPH) with the Docker provider (CAPD) as the local rehearsal environment — starting with the numbers.
The Before/After in One Table
Take a typical production cluster: 3 control-plane machines and 20 workers on Hetzner, upgrading from Kubernetes v1.31 to v1.34. Three scenarios, arithmetic from stated assumptions:
| (a) Pre-v1.12, naive | (b) Pre-v1.12, skew-aware | (c) v1.12 chained + in-place | |
|---|---|---|---|
| How workers are handled | Rolled on every hop | Deferred, rolled once at the end | One hop, in-place if the extension covers it |
| Machine replacements | 3 hops × 23 machines = 69 | 3×3 CP + 20 workers = 29 | 0–29 (see below) |
| Worker versions visited | v1.32, v1.33, v1.34 | v1.34 only | v1.34 only |
| Minor-version hops the operator drives by hand | 3 | 3 | 1 (declare v1.34 once) |
| Addon upgrades between hops | Manual | Manual | Lifecycle hooks |
Scenario (a) is what many teams actually do — bump the KubeadmControlPlane, then the MachineDeployments, repeat per minor. Scenario (b) is the honest pre-v1.12 baseline: since Kubernetes 1.28, the kubelet may lag the control plane by up to three minor versions, so a careful team defers worker upgrades on the intermediate hops and rolls workers once, at the target version. That gets you from 69 replacements down to 29 — but the operator still hand-sequences three control-plane hops and handles addons between each.
Scenario (c) is v1.12: the operator sets the version once. The control plane still walks each minor internally (9 machine updates), but each of those — and the single worker hop — can be an in-place update if the registered update extension covers the change, or falls back to a rollout if not. Full extension coverage: 0 replacements. Full fallback: 29 replacements — the same count as (b), but workers still make one hop instead of three, and the operator drove one declared version instead of three sequenced ones.
The variable that drives the result is extension coverage — whether your in-place update extension can handle a Kubernetes version bump on a live machine. The wins that hold even in the worst case: one declared hop instead of three, and addon steps automated by lifecycle hooks instead of a wiki page.
In-Place Updates: Three Hooks and a Feature Gate
In-place updates are not magic and not default. Everything hangs on the experimental InPlaceUpdates feature gate (off unless you enable it) and on a Runtime SDK extension you deploy, which implements three hooks:
CanUpdateMachine— called by the KubeadmControlPlane controller when it evaluates a pending change to a control-plane machine. The extension receives current and desired specs for the Machine, InfraMachine, and BootstrapConfig, and answers with patches describing which of the changes it can apply in-place.CanUpdateMachineSet— the MachineDeployment-side equivalent, operating on the MachineSet and its infrastructure/bootstrap templates rather than individual machines.UpdateMachine— the hook that does the work. It receives only the desired state and reports progress viaRetryAfterSeconds: a positive value means "still updating, check back," zero withStatus: Successmeans done, andFailureterminates the attempt.
The decision rule is strict and it is the single most important thing to understand before enabling the gate: the extension must cover the totality of the desired changes, or Cluster API falls back to a full rollout. There is no partial in-place update. If your extension can patch the kubelet version but the desired spec also changes the machine image, the machine gets replaced, exactly as before.
Two more constraints worth knowing before you write one:
- One extension only. The current implementation allows a single registered extension for the three hooks. You cannot compose a "kubelet updater" from one vendor with a "credential rotator" from another.
- Idempotency is on you.
UpdateMachinewill be re-invoked as it polls for completion. An extension that is not idempotent, deterministic, and timeout-disciplined can wedge updates fleet-wide — the docs are blunt about this.
Good in-place candidates are changes that never needed a node drain in the first place: Kubernetes component versions (kubeadm upgrades the node's binaries in place), container runtime configuration, rotating user credentials in the bootstrap config. Immutable rollouts remain the right mechanism for what they were designed for — swapping the OS image underneath a machine.
Chained Upgrades: Declare v1.34, Get the Plan
Chained upgrades live in the ClusterClass / managed-topologies world. If your clusters are plain Cluster objects without a topology, this feature does not apply to you until you migrate.
The mechanics: you change spec.topology.version on the Cluster to the target — even one that is several minors away. Cluster API computes an upgrade plan, either from the versions known to the ClusterClass or by calling a new runtime extension that supplies the plan, then executes it in a strictly controlled order. Two properties of that execution matter:
- The control plane walks each minor. Kubeadm's version-skew rules haven't changed; CAPI just stops making you drive each hop. Where an in-place extension covers the version bump, control-plane machines are patched rather than replaced.
- Workers skip intermediate minors whenever the Kubernetes version-skew policy allows. This is the codified version of the manual deferral trick from scenario (b) — except now it's the default behavior of the plan, not tribal knowledge in a runbook.
Existing lifecycle hooks were adapted to the new workflow and new ones added, so the "upgrade CNI/CSI addons after the control plane lands on each minor" step — the part most likely to be a manual checklist item — can be automated per plan step rather than remembered between hops.
What This Buys You on Hetzner (CAPH)
Replace-and-drain is not equally painful everywhere, and Hetzner is one of the places it hurts most.
On Hetzner Cloud instances, a replacement machine provisions in minutes. On Hetzner's dedicated (bare-metal) servers — the reason many teams are on Hetzner at all, given the price-per-performance — provisioning a replacement runs 10–30 minutes per machine: image installation, RAID setup, reboots. Multiply by the replacement counts in the table above and the difference between scenario (b) and a well-covered scenario (c) is measured in hours of maintenance window per upgrade cycle, plus the operational noise that comes with server churn: primary IPs released and reacquired, provider rate limits, capacity availability for the instance type you need at the moment you need it.
One version-compatibility note before you plan the jump: CAPH's v1.1.x line still implements the older v1beta1 provider contract and rides Cluster API's temporary compatibility layer (supported through CAPI v1.15 but explicitly not recommended long-term); the v1.2.x line is the v1beta2 contract generation aligned with CAPI v1.11+. Getting onto the v1beta2 line is effectively the price of admission for the v1.12 feature set — budget the provider upgrade before you budget the feature rollout.
Rehearse in CAPD Before You Touch Production
The under-appreciated half of the CAPH/CAPD pairing is that the Docker provider lets you rehearse this entire machinery on a laptop, with the same manifests you'll ship to production. Both features are gated and extension-dependent, which means there are new failure modes — an extension that reports success but never converges, a plan that falls back to rollouts you didn't expect — and you want to meet those in a kind cluster, not on a Saturday night in a Hetzner datacenter.
A concrete rehearsal loop:
- Stand up a CAPD management cluster with
InPlaceUpdatesenabled on the same CAPI version you'll run in production. - Deploy the same ClusterClass and the same update extension you intend to use with CAPH.
- Create a workload cluster at v1.31, then set
topology.versionto v1.34 and watch the plan execute end-to-end. - Deliberately test the fallback: include a change your extension does not cover and confirm CAPI performs a rollout instead of wedging.
- Only then promote the ClusterClass changes and the extension to the CAPH management cluster.
The feature-gate flags, the ClusterClass, and the extension registration are all provider-agnostic — which is exactly what makes the rehearsal representative.
The Runbook, Before and After
Here is the operational delta in runbook form. The "before" column is the skew-aware best practice (scenario b), not the naive one.
Before (CAPI v1.11 era, v1.31 → v1.34):
- Confirm etcd backup; check addon compatibility for v1.32.
- Bump KubeadmControlPlane to v1.32; wait for 3 control-plane machines to replace and drain.
- Defer MachineDeployment upgrades (workers stay on v1.31 — skew policy allows a 3-minor lag).
- Upgrade CNI/CSI/addons for v1.32 by hand.
- Repeat steps 1–4 for v1.33, then v1.34.
- Finally bump MachineDeployments to v1.34; wait for 20 workers to replace and drain.
- Total: 3 sequenced control-plane hops, 29 machine replacements, manual addon steps between every hop.
After (CAPI v1.12, same jump):
- Preflight once:
InPlaceUpdatesgate enabled on the management cluster; update extension deployed, registered, and rehearsed in CAPD; cluster on a ClusterClass; CAPH on the v1beta2 line; etcd backup as always. - Set
spec.topology.version: v1.34.xon the Cluster. - CAPI computes the plan and executes it: control plane walks v1.32 → v1.33 → v1.34 (in-place where covered), lifecycle hooks fire your addon automation per step, workers jump straight to v1.34.
- The operator's job shifts from driving to watching: monitor Machine conditions for in-place progress, and watch for fallback rollouts — a machine unexpectedly being replaced tells you the desired change exceeded extension coverage.
- Total: 1 declared hop, 0–29 replacements depending on extension coverage, addons automated.
The failure-handling posture also changes shape: before, a botched hop was a half-upgraded cluster and an operator deciding what to do at 2 a.m.; after, the dominant risks are a mis-built extension (mitigated by the CAPD rehearsal and idempotency discipline) and surprise fallbacks (mitigated by testing exactly which changes your extension covers).
What v1.12 Doesn't Change
An honest list, because upgrade machinery is the worst place for wishful thinking:
- Both features are experimental and gated.
InPlaceUpdatesis off by default; expect API evolution before graduation. - Immutable rollout remains the default and the fallback — and remains correct for image-based OS updates. In-place updates complement rollouts; they don't retire them.
- Version skew still governs everything. Chained upgrades sequence within the rules; they don't bend them.
- One extension slot. Until the single-extension limitation lifts, your in-place coverage is whatever one extension can do.
- ClusterClass is required for chaining. Teams on plain Cluster objects get in-place updates for KCP/MachineDeployments but no chained upgrades until they adopt managed topologies.
The Bigger Shift: Upgrades Become Declarative
Step back and the two features are one idea: the upgrade path itself is now something you declare, not something you perform. Pre-v1.12, Cluster API was declarative about machine state but procedural about getting between versions — the sequencing lived in your runbook and your calendar. v1.12 moves the sequencing into the controller, where the skew policy, the worker-skip optimization, and the addon hooks are code instead of checklist items. For a platform team running CAPH in production, the practical translation is: fewer maintenance windows, dramatically less drain churn on hardware where churn is expensive, and an upgrade that one declared field — rehearsed first in CAPD — can carry three minors.
That is also precisely the property that matters if the operator driving your fleet isn't always a human. A declarative, plan-computing upgrade is something an agent can safely initiate and monitor; a 14-step hand-sequenced runbook is not.
Bex.co is the open-source, AI-native Render alternative — push a git repo, get a running HTTPS service on machines you own, on a Cluster API-managed fleet. Star the repo on GitHub or deploy your first app today.
Sources: Cluster API v1.12: Introducing In-place Updates and Chained Upgrades (Kubernetes Blog) · Cluster API v1.12 (CNCF Blog) · Implementing In-Place Update Hooks (Cluster API Book) · In-place updates proposal · Cluster API v1.12.0 release · CAPH introduction and compatibility



