Upgrading Kubernetes on Bare Metal
This guide explains how to complete Phase 2 of the upgrade workflow for clusters on bare metal. Before you upgrade Kubernetes, complete the Distribution Version upgrade described in Upgrading Clusters.
Where this page fits in the full ACP upgrade flow
This page covers only the Kubernetes step of the upgrade. The full ACP upgrade flow — including upgrade artifact synchronization, ACP Core upgrade through CVO, Aligned plugin upgrades, and Agnostic plugin upgrades from Marketplace — is documented in the ACP product documentation. Complete those steps before you start the Kubernetes step on this page:
- Upgrade Overview (scope and sequencing)
- Pre-Upgrade Preparation
- Upgrade the global cluster (Core, Aligned, Agnostic)
- Upgrade workload clusters (Core, Aligned, Agnostic)
Use this page when the same cluster runs on a physical-host immutable operating system, because the Kubernetes step on bare-metal replaces every node from a new elemental upgrade image rather than upgrading binaries in place.
TOC
Key ConsiderationsUpgrade SequencePrerequisitesUpdate the Image CatalogUpgrade Kube-OVN Before the Control PlaneUpgrade the Control PlaneUpgrade WorkersCross-Version UpgradesVerificationTroubleshootingAdditional ResourcesKey Considerations
Bare-metal upgrades replace every node — they do not run kubeadm upgrade on the existing OS. The mechanism is:
- CAPI deletes one
Machineaccording to the rollout strategy. - The provider writes a
cleanplan to the inventory's plan secret. - The host stops kubelet + CRI workload + containerd, then returns to
Availablein the same pool. - CAPI creates a replacement
Machine. - The provider picks an
Availableinventory from the same pool, resolves the new Kubernetes version againstelemental-image-catalog, and writes areprovisionplan. - The host runs
cloud-init clean→elemental upgrade --system <new-image>→ reboot.initramfsclears Kubernetes persistent state; cloud-init re-executes and runskubeadm init/kubeadm joinagainst the new control plane.
Two structural consequences operators must internalize before starting:
- Bare-metal does not preserve Kubernetes-managed disk state.
/var/lib/kubelet,/var/lib/containerd,/var/lib/etcd,/etc/kubernetesare cleared by the initramfs cleanup step of every reprovision. There is no equivalent of the DCS provider's pool-managed persistent disks today. - The same
MachineInventorymay not be re-picked. The provider does not guarantee that the inventory released by acleanplan is the same one re-allocated to the replacementMachine. Replacements are performed as delete-then-add whenmaxSurge=0(no overlap between old and new node), so pool capacity sizing need only accommodate the desired replica count — old and new nodes do not coexist during the rollout.
Upgrade Sequence
Upgrade bare-metal clusters in the following order:
- (Prerequisite) Upgrade the ACP platform on the
globalcluster. This brings the bare-metal provider,elemental-operator, and the related CAPI components to versions that understand the new schema. Trigger workload-cluster upgrades only after the management-side controllers have rolled out and become Ready. - Upgrade the Distribution Version (Aligned Extensions) on the workload cluster. See Upgrading Distribution Version.
- Ensure the target Kubernetes version is present in
elemental-image-catalog(and the matching-isorepository is available for any future host re-registration). - Upgrade Kube-OVN to the chart version required by the target ACP release and wait for the
AppReleaseto reachSuccess. - Upgrade the control plane Kubernetes version (replaces all control-plane nodes one at a time).
- Upgrade worker nodes to the target Kubernetes version (replaces all worker nodes within the
maxUnavailablebudget).
Cluster API orchestrates the rolling replacement.
Skipping step 1 risks two failure modes: the old provider silently ignores new schema fields; or a controller image swap mid-rollout interrupts the plan secret state machine. Always settle the management-side upgrade before touching workload rollout.
Prerequisites
Before you start, ensure all of the following prerequisites are met:
- The Distribution Version upgrade is complete.
- The control plane is reachable through the existing
<control-plane-vip>:<control-plane-port>. - All current nodes are healthy and
Ready. - The target Kubernetes version is a key in
elemental-image-catalog. If it is not, add it before starting (see Update the Image Catalog). - The target kube-ovn (chart) version has been read from the matching ACP row in the OS Support Matrix.
BaremetalCluster.spec.networkTypeiskube-ovn; the provider skips Kube-OVN reconciliation for any other value.- The platform registry is reachable from every host in the cluster.
- Both
KubeadmControlPlane.spec.rolloutStrategy.rollingUpdate.maxSurge = 0andMachineDeployment.spec.strategy.rollingUpdate.maxSurge = 0are set — bare-metal does not over-provision physical hosts. - The relevant pools have enough capacity to replace one node at a time without falling below the desired replica count.
Update the Image Catalog
elemental-image-catalog is the resource that introduces a Kubernetes version to the bare-metal provider.
In most upgrades you do not edit this ConfigMap by hand. The bare-metal provider plugin re-renders elemental-image-catalog with the Kubernetes versions shipped by the new distribution when it is reapplied during the Distribution Version upgrade (Phase 1). Your task is normally just to verify that the target version is present (see the verification step below). The two options that follow are only needed when you must add an out-of-band version — for example a digest-pinned or test build that the plugin does not ship.
Option A — Add a chart override. Append the new version under provider.imageCatalog.images (uses global.registry.address as the registry):
Reapply the bare-metal provider plugin. The chart re-renders the ConfigMap; the provider's in-process watch hot-reloads the cache without restarting.
Option B — Patch the ConfigMap directly. Useful for digest-pinned images or out-of-band test versions:
In either case, verify before continuing:
The key must include the leading v (for example v1.34.5). If the key is missing when CAPI creates the replacement Machine, the resulting BaremetalMachine enters Failed with ImageResolved=False / Reason=ImageCatalogMiss and no reprovision plan is written — adding the key later restarts the reconciliation automatically.
When the new version corresponds to a new Alauda OS / base-image release, the ISO variant for that release should also be available before the upgrade. The bare-metal provider does not rebuild SeedImages automatically, but you may want to refresh MachineRegistration / SeedImage for future host onboarding so the new hosts come up on the new release. The ISO repository is the same as the base-image repository with -iso appended.
Upgrade Kube-OVN Before the Control Plane
Upgrade Kube-OVN and wait for it to become healthy before you change KubeadmControlPlane.spec.version. This ordering ensures that the target network components are already running before bare-metal control-plane nodes begin delete-reprovision replacement.
The bare-metal provider's reconciliation behavior makes two operations necessary:
- It reconciles Kube-OVN only when
BaremetalCluster.spec.networkTypeis exactlykube-ovn. - It reads
Cluster.metadata.annotations["cpaas.io/kube-ovn-version"]before falling back to thekube-ovnModulePluginversion. However, aftercni-kube-ovnalready exists, the provider updates onlyAppRelease.spec.valuesand preservesspec.source, including the existing charttargetRevision. Therefore, changing the annotation alone records the intended version but does not upgrade the existing chart.
-
Verify that provider-managed Kube-OVN reconciliation is enabled
Run this command against the management (
global) cluster:If the output is empty or different, correct
BaremetalCluster.spec.networkTypebefore continuing. Otherwise, the provider skips Kube-OVN reconciliation, including the control-plane node list updates needed during replacement. -
Record the target chart version on the CAPI
ClusterUse the kube-ovn (chart) value from the target ACP row in the OS Support Matrix:
Run this command against the management (
global) cluster where the CAPIClusterexists. The annotation takes precedence over the provider'sModulePluginfallback and keeps the cluster's recorded target deterministic. -
Patch the existing
AppReleasechart revisionRun this command against the workload cluster's API server, not the management cluster:
Use the same chart version that you set in the annotation. Do not change the provider-managed chart name (
acp/chart-cpaas-kube-ovn) or release name (cpaas-kube-ovn). -
Wait for reconciliation to complete before upgrading the control plane
The normal sequence is
Upgrading → HealthChecking → Success. Do not start the KCP rollout based oninstalledRevisionalone: it changes duringHealthChecking, before Kube-OVN pods have been verified Ready. Continue only whenphaseisSuccessandinstalledRevisionmatches the target chart version.If the
AppReleasereachesDownloadFailed,DeployFailed, orNotReady, stop before changingKubeadmControlPlaneand inspect the condition messages:
Upgrade the Control Plane
Before continuing, complete Upgrade Kube-OVN Before the Control Plane and verify that cni-kube-ovn is at the target installed revision with phase=Success.
Patch KubeadmControlPlane.spec.version to the new Kubernetes version. Where component image tags are pinned (DNS, etcd), update them in the same edit:
spec.version← target Kubernetes version (must match anelemental-image-catalogkey).spec.kubeadmConfigSpec.clusterConfiguration.dns.imageTag← matching CoreDNS image tag for the new release.spec.kubeadmConfigSpec.clusterConfiguration.etcd.local.imageTag← matching etcd image tag for the new release.- When the target is Kubernetes 1.35 or later, update
/etc/kubernetes/patches/kubeletconfiguration0+strategic.jsoninspec.kubeadmConfigSpec.filesin this same edit, as described in Required kubelet patch for Kubernetes 1.35.
The bare-metal provider does not require a new BaremetalMachineTemplate for a Kubernetes-only upgrade: the template only carries the pool reference, and the upgrade image is resolved from Machine.spec.version through the catalog. A new template is only required when you want to move the control plane to a different pool.
Cluster API rolls control-plane nodes one at a time (because maxSurge=0):
The expected sequence on each node:
- CAPI marks one old
Machinefor deletion; correspondingBaremetalMachinemoves toPreparing. - The provider writes a
cleanplan; the inventory ends upAvailablewith cleared owner annotations. - CAPI creates a new
Machine(with the target version); a newBaremetalMachineis created. - The provider allocates an
Availableinventory from the control-plane pool, resolves the new image, and writes areprovisionplan. - The host runs
elemental upgrade --system <new-image>, reboots, andkubeadm joins the surviving control plane.BaremetalMachine.status.phasebecomesRunning.
Repeat steps 1–5 until every control-plane node has been replaced.
Throughout the rollout, alive continues to manage the VIP. As control-plane membership changes, the bare-metal provider re-renders the alive chart values (the peer list and ipvs.ips) and rolls out the static-pod manifests.
Upgrade Workers
Patch each MachineDeployment.spec.template.spec.version to the new Kubernetes version. CAPI replaces workers within the maxUnavailable budget — the bare-metal provider re-uses the same worker pool and the same image-catalog resolution logic as the control plane.
For Kubernetes 1.35 or later, first create a new worker KubeadmConfigTemplate containing the required kubelet patch, then update spec.template.spec.bootstrap.configRef.name in the same MachineDeployment edit as the version bump. The command below includes that reference. For an earlier target with no bootstrap change, omit the bootstrap object. Follow Updating Bootstrap Templates to create the immutable template.
Watch:
Worker upgrades carry fewer knobs than the control plane: there is no clusterConfiguration block on MachineDeployment, so no DNS / etcd tags to update. Kubernetes component versions on worker nodes follow the new base image.
For earlier versions, a bootstrap-template change is needed only when the release changes settings such as cloud-init or kubeletExtraArgs.
Cross-Version Upgrades
Kubernetes control planes must move through supported minor-version hops. Use Preparing for Cross-Version Upgrades to identify and pre-stage every intermediate ACP row.
For each intermediate row, and finally for the target row:
- Ensure that row's Kubernetes version exists in
elemental-image-catalog. - Upgrade Kube-OVN to that row's kube-ovn (chart) version and wait for
phase=Success. - Upgrade the control plane to that row's Kubernetes version and wait for every control-plane node to become
Ready. - Upgrade every worker
MachineDeploymentto the same Kubernetes version and wait for the rollout to complete before starting the next hop.
The version values always come from the OS Support Matrix; they are not fixed examples in this guide. The bare-metal rollout strategy already serializes node replacement, so cross-version upgrades do not require additional pool capacity beyond what is needed for a same-minor upgrade.
Verification
After the rollout completes:
The cni-kube-ovn AppRelease should report the target installed revision with phase=Success. All BaremetalMachine objects should be Running. Every CAPI Machine should report the new spec.version. Every workload Node should be Ready with the new kubelet version, and every MachineInventory plan secret should carry baremetal.alauda.io/plan.type=reprovision (the most recent plan applied).
MachineInventoryPool.status should satisfy available + allocated + preparing + reprovisioning + unavailable = total with preparing = reprovisioning = 0.
Troubleshooting
For the full operator-side state machine reference (every condition reason and recovery action), see Provider Overview.