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.listExample 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.listUpdate the package information and check the available kubeadm versions:
sudo apt update
sudo apt-cache madison kubeadmYou 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 PackagesThe 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 kubeadmKubernetes 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 planThis 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.1Wait 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.
Step 5: Drain the Control Plane Node
Drain the node before upgrading kubelet:
kubectl drain controlplane --ignore-daemonsetsThis 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 kubectlRestart kubelet:
sudo systemctl daemon-reload
sudo systemctl restart kubeletCheck its status:
systemctl status kubeletStep 7: Uncordon the Node
Once kubelet is running, allow the node to schedule workloads again:
kubectl uncordon controlplaneFor 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.listStep 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 kubectlStep 3: Update the Node Configuration
Run:
sudo kubeadm upgrade nodeOn 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-daemonsetsThis 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 kubeletRestart kubelet:
sudo systemctl daemon-reload
sudo systemctl restart kubeletStep 6: Uncordon the Worker
Once the node is healthy, allow it to schedule workloads again:
kubectl uncordon node01Repeat 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 nodesExpected output:
$ kubectl get nodes
NAME STATUS ROLES AGE VERSION
controlplane Ready control-plane 7m47s v1.37.1
node01 Ready <none> 7m37s v1.37.1Every node should show Ready with the expected Kubernetes version.
Check the system Pods:
kubectl get pods -n kube-systemAlso 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.
