A Kubernetes cluster is running smoothly, and everything looks fine. Then comes the task of upgrading it to a newer version.

You start by checking the available kubeadm packages: apt-cache madison kubeadm

But the required version does not appear. Only the currently installed version is listed.

The reason could be simple. The package repository is still pointing to the previous Kubernetes minor version. Until you update it, the new packages will not appear.

This is a common issue during a kubeadm upgrade. But finding the correct package is only the beginning.

You must also upgrade the control plane and worker nodes in the correct order.

This guide explains how to upgrade a Kubernetes cluster from v1.36 to v1.37.1 using kubeadm.

It covers the repository update, control plane upgrade, worker node upgrade, and final verification.

Why Upgrade Your Kubernetes Cluster?

Kubernetes releases new versions with security fixes, bug fixes, and improvements. Running an unsupported version means missing important updates.

The Kubernetes project maintains only the three most recent minor releases. Each release receives patch updates during its support period.

Version Latest patch End of life
1.37 1.37.1 Oct 28, 2027
1.36 1.36.5 Jun 28, 2027
1.35 1.35.9 Feb 28, 2027
1.34 1.34.12 Oct 27, 2026

👉 Source: Kubernetes Releases

Kubernetes v1.37 also introduces several improvements. KYAML and the metrics.k8s.io API have reached Stable.

SELinux-related features have also graduated to Stable, while HPA scale-to-zero has moved to Beta.

Prerequisites

Before starting the upgrade, check the following:

  • Version sequence: Upgrade one minor version at a time. A v1.35 cluster must first move to v1.36 before upgrading to v1.37.
  • Backup: Take an etcd snapshot and back up important application data.
  • Swap: Make sure swap is disabled on every node.
  • Release notes: Review removed APIs and changed defaults.
  • Package repository: Make sure the nodes use pkgs.k8s.io.

Check the repository configuration:

cat /etc/apt/sources.list.d/kubernetes.list

Example output:

deb [signed-by=/etc/apt/keyrings/kubernetes-apt-keyring.gpg] https://pkgs.k8s.io/core:/stable:/v1.36/deb/ /

Notice the v1.36 value. Each Kubernetes minor version has a separate repository. That is why the v1.37 packages aren't available yet.

Note: Tested on Kubernetes v1.36.5 and v1.37.1 using kubeadm on Ubuntu 24.04.3 LTS with CRI-O and Calico.

Part 1: Upgrade the Control Plane with Kubeadm

The first control plane node applies the cluster upgrade using kubeadm upgrade apply. In this example, the node is named controlplane.

Step 1: Update the Package Repository

Change the repository from v1.36 to v1.37:

sudo sed -i 's/v1.36/v1.37/' /etc/apt/sources.list.d/kubernetes.list

Update the package information and check the available kubeadm versions:

sudo apt update
sudo apt-cache madison kubeadm

You get flowing output:

Reading package lists...
Building dependency tree...
Reading state information...
224 packages can be upgraded. Run 'apt list --upgradable' to see them.
    kubeadm | 1.37.1-1.1 | https://pkgs.k8s.io/core:/stable:/v1.37/deb Packages
    kubeadm | 1.37.0-1.1 | https://pkgs.k8s.io/core:/stable:/v1.37/deb Packages

The v1.37.1 package should now appear.

Step 2: Upgrade kubeadm

Install the required kubeadm version:

sudo apt-mark unhold kubeadm && \
sudo apt-get update && sudo apt-get install -y kubeadm='1.37.1-*' && \
sudo apt-mark hold kubeadm

Kubernetes packages are held to prevent accidental upgrades during regular system updates.
You can see this output:

$ sudo apt-mark unhold kubeadm && \
    sudo apt-get update && sudo apt-get install -y kubeadm='1.37.1-*' && \
    sudo apt-mark hold kubeadm

Canceled hold on kubeadm.
Hit:1 http://ports.ubuntu.com/ubuntu-ports noble InRelease
Hit:2 https://prod-cdn.packages.k8s.io/repositories/isv:/kubernetes:/core:/stable:/v1.37/deb InRelease
Hit:3 http://ports.ubuntu.com/ubuntu-ports noble-updates InRelease
Hit:4 https://download.opensuse.org/repositories/isv:/cri-o:/stable:/v1.36/deb InRelease
Hit:5 http://ports.ubuntu.com/ubuntu-ports noble-backports InRelease
Hit:6 http://ports.ubuntu.com/ubuntu-ports noble-security InRelease
Reading package lists...
[... output trimmed ...]

No containers need to be restarted.

No user sessions are running outdated binaries.

No VM guests are running outdated hypervisor (qemu) binaries.
kubeadm set on hold.

Verify the installed version:

$ kubeadm version

kubeadm version: &version.Info{Major:"1", Minor:"37", EmulationMajor:"", EmulationMinor:"", MinCompatibilityMajor:"", MinCompatibilityMinor:"", GitVersion:"v1.37.1", GitCommit:"f78e722310e50bcaca9276be22276d9e91d91308", GitTreeState:"clean", BuildDate:"2026-09-23T17:09:17Z", GoVersion:"go1.26.8", Compiler:"gc", Platform:"linux/arm64"}

Step 3:Run the Kubeadm Upgrade Plan

Before applying the upgrade, check whether the cluster is ready:

sudo kubeadm upgrade plan

This command displays the current and available upgrade versions. Review the output before continuing.

Step 4: Apply the Upgrade

Upgrade the control plane:

sudo kubeadm upgrade apply v1.37.1

Wait for the command to finish. A successful upgrade should display:

config-2026-10-18-07-21-24 for kubelet config. To override it set the environment variable
KUBEADM_UPGRADE_DRYRUN_DIR
[patches] Applied patch of type "application/strategic-merge-patch+json" to target "kubeletconfiguration"
[kubelet-start] Writing kubelet configuration to file "/var/lib/kubelet/config.yaml"
[upgrade/kubelet-config] The kubelet configuration for this node was successfully upgraded!
[upgrade/bootstrap-token] Configuring bootstrap token and cluster-info RBAC rules
[bootstrap-token] Configured RBAC rules to allow Node Bootstrap tokens to get nodes
[bootstrap-token] Configured RBAC rules to allow Node Bootstrap tokens to post CSRs in order for nodes to get long term certificate credentials
[bootstrap-token] Configured RBAC rules to allow the csrapprover controller automatically approve CSRs from a Node Bootstrap Token
[bootstrap-token] Configured RBAC rules to allow certificate rotation for all node client certificates in the cluster
[bootstrap-token] Configured RBAC rules to allow the API server kubelet client certificate to access the kubelet API
[addons] Applied essential addon: CoreDNS
[addons] Applied essential addon: kube-proxy

[upgrade] SUCCESS! A control plane node of your cluster was upgraded to "v1.37.1".

[upgrade] Now please proceed with upgrading the rest of the nodes by following the right order.

kubeadm also renews the certificates it manages during an upgrade. This can be disabled with --certificate-renewal=false when required.

⚠️
Note: kubeadm does not upgrade CNI plugins automatically. Upgrade Calico, Cilium, or other network plugins separately by following their official upgrade documentation.

Step 5: Drain the Control Plane Node

Drain the node before upgrading kubelet:

kubectl drain controlplane --ignore-daemonsets

This marks the node as unschedulable and evicts eligible Pods so they can run elsewhere.

Step 6: Upgrade kubelet and kubectl

Install the required versions:

sudo apt-mark unhold kubelet kubectl && \
sudo apt-get update && sudo apt-get install -y kubelet='1.37.1-*' kubectl='1.37.1-*' && \
sudo apt-mark hold kubelet kubectl

Restart kubelet:

sudo systemctl daemon-reload
sudo systemctl restart kubelet

Check its status:

systemctl status kubelet

Step 7: Uncordon the Node

Once kubelet is running, allow the node to schedule workloads again:

kubectl uncordon controlplane

For additional control plane nodes, use kubeadm upgrade node instead of kubeadm upgrade apply. Upgrade each node one at a time.

Part 2: Upgrade the Worker Nodes

The control plane is now upgraded. The next step is to upgrade the worker nodes.

Worker nodes use kubeadm upgrade node. They do not run kubeadm upgrade apply. Upgrade one worker at a time so the remaining nodes can continue running workloads.

In this example, the worker is named node01.

Step 1: Update the Package Repository

Update the repository on the worker node:

sudo sed -i 's/v1.36/v1.37/' /etc/apt/sources.list.d/kubernetes.list

Step 2: Upgrade kubeadm and kubectl

Install the required versions:

sudo apt-mark unhold kubeadm kubectl && \
sudo apt-get update && sudo apt-get install -y kubeadm='1.37.1-*' kubectl='1.37.1-*' && \
sudo apt-mark hold kubeadm kubectl

Step 3: Update the Node Configuration

Run:

sudo kubeadm upgrade node

On a worker node, this updates the local kubelet configuration.

Step 4: Drain the Worker

Run this command from the control plane:

kubectl drain node01 --ignore-daemonsets

This makes the node unschedulable and evicts eligible Pods.

Step 5: Upgrade kubelet

Install the new kubelet package:

sudo apt-mark unhold kubelet && \
sudo apt-get update && sudo apt-get install -y kubelet='1.37.1-*' && \
sudo apt-mark hold kubelet

Restart kubelet:

sudo systemctl daemon-reload
sudo systemctl restart kubelet

Step 6: Uncordon the Worker

Once the node is healthy, allow it to schedule workloads again:

kubectl uncordon node01

Repeat these steps for every remaining worker node.

[Image: Show a worker node moving through the drain, upgrade, and uncordon process.]

Part 3: Verify the Upgrade

After upgrading all nodes, check their status:

kubectl get nodes

Expected output:

$ kubectl get nodes

NAME           STATUS   ROLES           AGE     VERSION
controlplane   Ready    control-plane   7m47s   v1.37.1
node01         Ready    <none>           7m37s   v1.37.1

Every node should show Ready with the expected Kubernetes version.

Check the system Pods:

kubectl get pods -n kube-system

Also verify the client and server versions:

$ kubectl version

Client Version: v1.37.1
Kustomize Version: v5.8.1
Server Version: v1.37.1
vagrant@controlplane:~$

Finally, check the application workloads and confirm that important services are healthy.

Common Kubernetes Upgrade Issues

A few problems can interrupt the upgrade.

Issue Possible cause Solution
New version not listed Old repository URL Update to the v1.37 repository
Packages not found Old apt.kubernetes.io repository Migrate to pkgs.k8s.io
Upgrade rejected Skipping a minor version Upgrade one minor version at a time
Pods stuck during drain PodDisruptionBudget blocks eviction Check PDBs and available replicas
Node becomes NotReady kubelet failed to start Check journalctl -xeu kubelet
Pod networking fails CNI compatibility issue Check the CNI upgrade procedure

If an upgrade fails, do not immediately repeat all the commands. Check which step failed and inspect the current cluster state before continuing.

FAQs


I have listed the questions you are likely to receive frequently below.

Can Kubernetes be upgraded directly from v1.35 to v1.37?

No. Upgrade from v1.35 to v1.36 first, then move to v1.37.

What is the difference between kubeadm upgrade apply and kubeadm upgrade node?

kubeadm upgrade apply applies the cluster upgrade on the first control plane node. kubeadm upgrade node updates the local configuration on additional control plane and worker nodes.

Does the repository need to change for a patch upgrade?

No. The repository is configured per minor version. Patch releases within v1.37 use the same v1.37 repository.

How can application downtime be reduced?

Run multiple application replicas, configure suitable PodDisruptionBudgets, and upgrade worker nodes one at a time.

Multiple control plane nodes also help maintain API availability during maintenance.

Conclusion

The main challenge in a kubeadm upgrade is not the commands themselves. It is following the correct order and preparing each node properly.

Update the package repository, upgrade the control plane first, and then move to the worker nodes one at a time. Drain each node before upgrading kubelet and verify its health before allowing workloads back.

Once you understand this process, future Kubernetes upgrades become much easier to manage.