Kubernetes v1.37.0 went generally available on August 26, 2026, and it is not a quiet release. It deprecates IPVS as a kube-proxy mode, flips SELinuxMount on by default, removes static pod secret support, and promotes the metrics.k8s.io API to stable v1. If you run production clusters on EKS, AKS, GKE, or bare metal, at least one of those four changes will touch your setup. This tutorial walks through a full, tested migration path from Kubernetes 1.34 or 1.35/1.36 to 1.37.0, covering the breaking changes, the pre-flight checks, the actual upgrade commands, and the rollback plan you need before you start.
Kubernetes 1.34 entered maintenance mode with its final patch, 1.34.11, released August 11, 2026. That means 1.34 clusters are no longer getting active feature backports, and the clock on full end-of-life is running. The 1.37 line carries standard support with an end-of-life date of October 28, 2027, according to the Kubernetes release information page. If you are still two versions behind, this guide also covers the skip-version upgrade path, since Kubernetes only supports upgrading one minor version at a time.
Don't miss new tech stories on Google
Add Tech Insider once in the Google app and our stories appear in your news suggestions.
Prerequisites and Version Requirements
Before touching a production cluster, confirm your tooling matches what 1.37 expects. The Kubernetes project only guarantees skew compatibility within one minor version between control plane and kubelet, and kubectl should stay within one minor version of the API server in either direction.
- kubectl version 1.36.x or 1.37.x (matching or one minor version off your target cluster)
- kubeadm 1.37.0, if you manage a self-hosted or on-prem cluster
- etcd 3.5.16 or later, required for the metrics.k8s.io v1 promotion path
- containerd 1.7.20+ or CRI-O 1.31+ (check your node image’s container runtime version)
- Helm 3.16 or later, if any workloads are Helm-managed
- A current cluster on 1.34.x, 1.35.x, or 1.36.x — 1.37 upgrades must come from one of these three lines
- A tested etcd snapshot and velero (or equivalent) backup taken within the last hour before you start
- Admin access to your cloud provider’s managed control plane console (EKS, AKS, or GKE) if you’re not self-managing
If you’re on a managed service, check your provider’s supported version list first. AWS, Google, and Microsoft typically lag the open-source release by four to eight weeks before adding a new minor version to their managed offering, so EKS, GKE, and AKS may not expose 1.37 as selectable yet at the time you read this. Run the version-check commands in Step 1 to confirm what’s actually available to you before planning a date.
Step 1: Audit Your Current Cluster Version and API Usage
Start by confirming exactly what you’re running today, on every node, not just the control plane. Version drift between nodes is the single most common cause of a failed upgrade.
kubectl version --short
kubectl get nodes -o custom-columns=NAME:.metadata.name,VERSION:.status.nodeInfo.kubeletVersion,OS:.status.nodeInfo.osImage,RUNTIME:.status.nodeInfo.containerRuntimeVersion
# Check for deprecated API usage cluster-wide before you upgrade
kubectl get --raw /metrics | grep apiserver_requested_deprecated_apis
The second command surfaces every deprecated API call your cluster has served recently, which is exactly what you need before jumping a minor version. If you manage a managed control plane, run the equivalent provider check: aws eks describe-cluster, az aks show, or gcloud container clusters describe, and compare the reported kubernetesVersion against the list of versions each provider currently supports for new upgrades.
Step 2: Understand the Three Breaking Changes in Kubernetes 1.37
Three changes in 1.37 will break workloads that aren’t ready for them. None of them are exotic edge cases — they hit common patterns in networking, storage, and static pod configuration.
IPVS kube-proxy mode is deprecated
IPVS (IP Virtual Server) has been a kube-proxy mode option since 2016, offering better performance than iptables at very large service counts. Kubernetes 1.37 marks it deprecated, which means it still works in this release but will eventually be removed, per the project’s own API deprecation guide. If your kube-proxy ConfigMap sets mode: ipvs, plan a migration to nftables mode (the newer default) or confirm your CNI’s built-in service proxy already replaces kube-proxy entirely, which is increasingly common with Cilium and Calico eBPF-based dataplanes.
SELinuxMount is enabled by default
SELinuxMount changes how the kubelet applies SELinux labels to mounted volumes, using mount options instead of a full recursive relabel of every file. On RHEL-family nodes (RHEL, Rocky, Alma) where SELinux is enforcing, this speeds up pod startup for large volumes significantly, but it also means any workload that depended on the old recursive-relabel timing or custom SELinux contexts applied out-of-band needs re-testing. Clusters running with SELinux disabled or permissive are unaffected.
Static pod secrets are removed
This is the change most likely to cause a hard failure. Static pods defined directly on a node via the kubelet’s manifest path can no longer reference Secret or ConfigMap volumes the way they could in earlier releases. If you run static pods for control plane components (common in kubeadm-based clusters for etcd, kube-apiserver, kube-controller-manager, and kube-scheduler) that reference certificates or credentials through secret volumes rather than host-mounted file paths, they will fail to start after the upgrade. Audit every static pod manifest under /etc/kubernetes/manifests/ on each control plane node before you proceed.
# Run on every control-plane node to find static pods referencing secrets
grep -A3 "secret:" /etc/kubernetes/manifests/*.yaml
# Cluster-wide check for kube-proxy mode
kubectl get configmap kube-proxy -n kube-system -o yaml | grep mode
Step 3: Review the metrics.k8s.io Stable API Promotion
The metrics.k8s.io API, used by the Horizontal Pod Autoscaler and by kubectl top, moves from beta to stable v1 in 1.37. This is a positive change (a stable API gets stronger backward-compatibility guarantees), but any Helm chart, Terraform module, or custom autoscaler config that hardcodes metrics.k8s.io/v1beta1 in an API request or RBAC rule should be updated to reference v1. Metrics-server itself (the most common backend for this API) has published a compatible release; confirm your deployed version is 0.8.0 or later before upgrading the control plane, since older metrics-server builds may not register the stable API group correctly.
# Check which API version your HPA and tooling actually hit
kubectl get apiservices | grep metrics.k8s.io
# Confirm metrics-server version
kubectl get deployment metrics-server -n kube-system -o jsonpath='{.spec.template.spec.containers[0].image}'
Step 4: Build Your Pre-Upgrade Backup
Every rollback plan starts with a clean etcd snapshot and a full workload backup. Do not skip this step even if you’re upgrading a managed control plane where the cloud provider handles etcd for you — you still want an application-level backup in case a workload-facing breaking change (like the static pod secret removal) corrupts state.
# Self-managed etcd snapshot (run from a control-plane node)
ETCDCTL_API=3 etcdctl --endpoints=https://127.0.0.1:2379 \
--cacert=/etc/kubernetes/pki/etcd/ca.crt \
--cert=/etc/kubernetes/pki/etcd/server.crt \
--key=/etc/kubernetes/pki/etcd/server.key \
snapshot save /backup/etcd-snapshot-$(date +%Y%m%d-%H%M).db
# Verify the snapshot is valid before trusting it
ETCDCTL_API=3 etcdctl snapshot status /backup/etcd-snapshot-*.db --write-out=table
# Velero backup for workload-level state (works on any provider)
velero backup create pre-137-upgrade --include-cluster-resources=true --wait
Store the etcd snapshot somewhere off-cluster, such as S3, GCS, or Azure Blob Storage, not just on the local node disk. If the node that produced the snapshot is also the node that fails during upgrade, a local-only backup is worthless.
Step 5: Stage the Upgrade in a Non-Production Cluster First
Clone your production configuration into a staging cluster, or at minimum a kind or minikube cluster running representative workloads, and run the full upgrade sequence there before touching anything customers depend on. This is where the static pod secret removal and SELinuxMount changes will actually surface, since they depend on your specific manifests and node OS, not on generic release notes.
# Quick local reproduction with kind, pinned to your current and target versions
kind create cluster --name pre-137-check --image kindest/node:v1.36.4
# ... apply your production manifests here, confirm baseline health ...
kind create cluster --name post-137-check --image kindest/node:v1.37.0
# ... apply the same manifests, diff the results ...
Give this staging run at least 48 hours of soak time if you have any batch jobs, CronJobs, or scheduled scaling events. A surprising number of upgrade failures only appear when a specific CronJob fires for the first time post-upgrade, not during the upgrade window itself.
Step 6: Upgrade the Control Plane (Self-Managed with kubeadm)
If you’re running kubeadm, upgrade the first control-plane node first, verify it, then repeat for the remaining control-plane nodes one at a time. Never upgrade all control-plane nodes simultaneously. The official kubeadm upgrade documentation covers additional flags for HA topologies with external etcd, which this walkthrough assumes is stacked (etcd co-located with the control plane).
# On the first control-plane node
apt-get update && apt-get install -y kubeadm=1.37.0-1.1
kubeadm upgrade plan
kubeadm upgrade apply v1.37.0
# Upgrade kubelet and kubectl on that same node
apt-get install -y kubelet=1.37.0-1.1 kubectl=1.37.0-1.1
systemctl daemon-reload
systemctl restart kubelet
# Verify before moving to the next node
kubectl get nodes
kubectl get pods -n kube-system
For additional control-plane nodes, use kubeadm upgrade node instead of kubeadm upgrade apply, since only the first node applies the new cluster configuration. Repeat the kubelet and kubectl package upgrade on each one afterward.
Step 7: Upgrade Managed Control Planes (EKS, AKS, GKE)
Managed Kubernetes offerings handle the control-plane upgrade for you, but the commands and rollout order differ by provider. All three require you to check version availability first, since new minor versions roll out to managed fleets gradually. AWS documents the full process in its EKS cluster update guide, and Google publishes an equivalent GKE cluster upgrade guide covering both master and node pool upgrade flows.
# Amazon EKS — check availability, then upgrade
aws eks describe-addon-versions --kubernetes-version 1.37 --query 'addons[].addonName'
aws eks update-cluster-version --name my-cluster --kubernetes-version 1.37
# Azure AKS
az aks get-upgrades --resource-group my-rg --name my-cluster -o table
az aks upgrade --resource-group my-rg --name my-cluster --kubernetes-version 1.37.0
# Google GKE
gcloud container get-server-config --zone us-central1-a --format="value(validMasterVersions)"
gcloud container clusters upgrade my-cluster --master --cluster-version 1.37.0-gke.100
On all three providers, the control plane upgrades independently from node pools. Do not upgrade node pools until the control plane upgrade reports healthy, since a control plane running 1.37 with nodes still on 1.34 exceeds the supported one-minor-version skew and can cause scheduling and admission-control inconsistencies.
Step 8: Roll Node Pools Using Surge or Blue-Green Replacement
Once the control plane is confirmed healthy on 1.37, upgrade worker nodes. The safest pattern for stateful or latency-sensitive workloads is surge replacement: bring up new nodes on the target version, cordon and drain old nodes gradually, then terminate them once workloads have rescheduled successfully.
# Cordon and drain a node before removing it from the pool
kubectl cordon node-old-01
kubectl drain node-old-01 --ignore-daemonsets --delete-emptydir-data --timeout=300s
# EKS managed node group surge upgrade
aws eks update-nodegroup-version --cluster-name my-cluster --nodegroup-name my-nodegroup \
--kubernetes-version 1.37
# Confirm every node reports the new version before continuing
kubectl get nodes -o custom-columns=NAME:.metadata.name,VERSION:.status.nodeInfo.kubeletVersion
Set your surge configuration (max unavailable, max surge) conservatively for the first production run. A max surge of 25% with max unavailable of 0 is a reasonable starting point for most clusters, keeping full capacity available throughout the rollout at the cost of a slightly slower upgrade.
Step 9: Fix the IPVS, SELinuxMount, and Static Pod Issues You Flagged in Step 2
With the cluster running 1.37, go back to what Step 2 surfaced and apply the fixes.
- IPVS clusters: switch
kube-proxymode tonftablesor confirm your eBPF-based CNI already bypasses kube-proxy. Test failover behavior under load before removing IPVS configuration entirely. - SELinuxMount-affected nodes: re-run your workload test suite on RHEL-family nodes with SELinux enforcing, paying attention to any custom SELinux contexts applied via init containers or admission webhooks.
- Static pod secrets: rewrite affected static pod manifests to reference host-mounted file paths (via
hostPathvolumes pointing at files already placed on disk by your configuration management tool) instead of Secret volumes.
# Example: converting a static pod secret reference to hostPath
# Before (1.36 and earlier):
# volumes:
# - name: etcd-certs
# secret:
# secretName: etcd-certs
# After (1.37-compatible):
# volumes:
# - name: etcd-certs
# hostPath:
# path: /etc/kubernetes/pki/etcd
# type: Directory
Step 10: Update the metrics.k8s.io References in Your Tooling
Update Helm charts, Terraform modules, and any custom controller code that hardcodes v1beta1 for the metrics API. Most mainstream Helm charts for metrics-server and Vertical Pod Autoscaler had already added v1 support ahead of the 1.37 GA date, so a chart version bump often resolves this automatically. Confirm with a dry-run before applying to production.
helm repo update
helm show values metrics-server/metrics-server | grep -i apiVersion
helm upgrade metrics-server metrics-server/metrics-server -n kube-system --dry-run
Step 11: Validate Cluster Health Post-Upgrade
Run a structured validation pass rather than eyeballing the dashboard. Check control-plane component health, node readiness, workload rollout status, and autoscaler function, in that order.
# Control plane and node health
kubectl get componentstatuses 2>/dev/null || kubectl get --raw /healthz/etcd
kubectl get nodes
kubectl get pods -n kube-system -o wide
# Workload rollout status across all deployments
kubectl get deployments --all-namespaces -o json | \
jq -r '.items[] | select(.status.readyReplicas != .status.replicas) | .metadata.namespace + "/" + .metadata.name'
# Confirm HPA is reading metrics correctly under the new stable API
kubectl get hpa --all-namespaces
kubectl top nodes
kubectl top pods --all-namespaces
An empty result from the deployment-rollout check means every deployment has its desired replica count ready — that’s your green light. If kubectl top returns errors, the metrics-server API registration is the first place to look, tying back to Step 10.
Step 12: Document the Rollback Path (Even If You Don’t Use It)
Kubernetes does not officially support downgrading a control plane’s minor version in place. If the upgrade fails badly enough to require rollback, your real recovery path is restoring the etcd snapshot from Step 4 onto freshly provisioned 1.36 control-plane nodes, or — for managed services — opening a support case, since AWS, Google, and Microsoft handle control-plane rollback differently and some do not offer self-service downgrade at all. Write this procedure down and rehearse it in staging before your production maintenance window, not during an incident.
| Provider | Self-service control-plane downgrade? | Recommended recovery path |
|---|---|---|
| Self-managed (kubeadm) | No | Restore etcd snapshot to new 1.36 control-plane nodes |
| Amazon EKS | No | Open AWS Support case; restore workloads from Velero backup to a new 1.36 cluster if urgent |
| Azure AKS | No | Open Azure Support case; redeploy from IaC to a fresh AKS cluster on prior version |
| Google GKE | No | Open Google Cloud Support case; GKE retains recent control-plane backups internally |
CNI-Specific Considerations: Cilium, Calico, and Flannel
The IPVS deprecation lands differently depending on which CNI you run, so treat Step 9’s kube-proxy fix as CNI-specific rather than universal. Clusters running Cilium in kube-proxy-replacement mode never touch IPVS or iptables in the first place, since Cilium’s eBPF dataplane handles service routing directly at the kernel level. If that’s your setup, the IPVS deprecation is a non-event; confirm it by checking cilium status for KubeProxyReplacement: Strict or True before you skip this section entirely.
Calico users have two paths depending on configuration. Calico’s own eBPF dataplane mode bypasses kube-proxy the same way Cilium does, but the more common standard mode still relies on kube-proxy running in either iptables or IPVS mode underneath Calico’s network policy layer. Check your Calico installation manifest or Helm values for felixConfiguration.bpfEnabled; if it’s false or unset, you’re still running standard kube-proxy and need to plan the nftables migration described in Step 9.
Flannel does not replace kube-proxy at all, so any Flannel-based cluster running IPVS mode needs the full migration path. Flannel is common on smaller self-managed clusters and homelab-style deployments, where kube-proxy’s ConfigMap is often left at whatever default the cluster bootstrap tool set years ago. Run the kube-proxy mode check from Step 2 explicitly rather than assuming, since long-lived Flannel clusters are the most likely to still be on IPVS from a configuration choice made in 2019 or 2020.
| CNI | Bypasses kube-proxy? | IPVS deprecation impact |
|---|---|---|
| Cilium (kube-proxy-replacement) | Yes, via eBPF | None — verify with cilium status |
| Calico (eBPF mode) | Yes, via eBPF | None — verify felixConfiguration.bpfEnabled |
| Calico (standard mode) | No | Full migration required if kube-proxy mode is IPVS |
| Flannel | No | Full migration required if kube-proxy mode is IPVS |
Monitoring and Alerting Through the Upgrade Window
Treat the upgrade window itself as an observability event, not just a change to wait out. Before you start, add a temporary annotation or deployment marker to your monitoring stack (Grafana annotations, Datadog events, or an equivalent) so that any anomaly showing up in the following hours is easy to correlate against the exact upgrade timestamp rather than guessing later. Widen alert thresholds slightly for node-not-ready and pod-restart alerts during the maintenance window itself, since cordoning and draining nodes will trigger expected, benign versions of both, but do not disable these alerts entirely — a genuine failure during the upgrade is exactly when you need them most.
After the upgrade completes, keep the widened thresholds in place for roughly 24 to 48 hours rather than reverting immediately, since some of the pitfalls covered above (CronJob failures, PDB-related stalls, SELinux AVC denials) surface hours after the last node reports Ready rather than during the upgrade itself. If you use Prometheus, watch apiserver_request_duration_seconds and etcd_request_duration_seconds for any sustained latency increase, which can indicate the new SELinuxMount behavior or metrics API registration is putting unexpected load on the control plane.
Cost and Capacity Planning Around the Upgrade
The surge replacement pattern in Step 8 has a direct cost line item that’s easy to overlook when planning the maintenance window: running new nodes alongside old ones during the drain-and-replace cycle temporarily increases your compute spend, and on managed node groups with autoscaling enabled, a max-surge setting of 25% on a 40-node pool means paying for up to 10 extra nodes for the duration of the rollout. For most teams that’s a rounding error measured in hours, but if you’re running large GPU-backed node pools for AI or ML workloads, the same surge percentage on expensive instance types can add up quickly. Consider dropping max surge to a smaller percentage, or scheduling GPU node pool upgrades during a planned low-utilization window, to keep the temporary cost bump manageable.
On the capacity side, factor in that SELinuxMount’s default-on behavior in 1.37 reduces pod startup latency on SELinux-enforcing nodes with large persistent volumes, since the kubelet no longer performs a full recursive relabel on every mount. If your autoscaler’s scale-up responsiveness has been a bottleneck during traffic spikes specifically because of slow volume-mount times, this change is worth validating in staging as a potential, incidental improvement to your scale-up latency, separate from the migration itself.
Kubernetes 1.34 vs 1.35/1.36 vs 1.37: What Changed at Each Step
If you’re jumping more than one minor version, you need to understand the cumulative changes, not just what 1.37 adds. Kubernetes only supports sequential minor-version upgrades (1.34 to 1.35, then 1.35 to 1.36, then 1.36 to 1.37), so plan for three separate upgrade windows if you’re starting from 1.34.
| Version | Release date | Status | Key change relevant to migration |
|---|---|---|---|
| 1.34 | Mid-2026 | Maintenance mode (final patch 1.34.11, Aug 11, 2026) | Last line before the IPVS/SELinuxMount/static pod changes land |
| 1.35 / 1.36 | 2026 | Standard support | Intermediate feature-gate changes; review your own changelog notes for each |
| 1.37.0 | August 26, 2026 | Standard support (active) | IPVS deprecated, SELinuxMount default-on, static pod secrets removed, metrics.k8s.io stable v1 |
Kubernetes 1.37.0 shipped after release candidates rc.0 on August 6, 2026 and rc.1 on August 19, 2026, giving the community roughly three weeks of pre-release testing. The August patch cycle for the broader project was also delayed to August 19 due to GitHub infrastructure disruptions that affected the automated cherry-pick process used for backporting fixes to older lines, which is worth knowing if you noticed patch releases arriving later than your usual schedule that month.
Common Pitfalls During a Kubernetes 1.37 Migration
These are the mistakes that turn a routine minor-version bump into an incident. Each one is avoidable if you check for it explicitly before your maintenance window.
- Skipping the skew check between kubectl and the API server. A kubectl client more than one minor version away from the server will silently fail on some commands or return incomplete output rather than a clear error.
- Upgrading node pools before the control plane finishes. This temporarily exceeds the supported version skew and can cause pods to schedule onto nodes with incompatible feature gates.
- Not auditing static pod manifests on every control-plane node individually. Manifests can drift between nodes if they weren’t deployed through a single source of truth, so checking one node isn’t enough.
- Assuming your CNI already handles the IPVS deprecation. Only eBPF-based dataplanes (Cilium in kube-proxy-replacement mode, Calico eBPF mode) bypass kube-proxy entirely. Standard iptables-mode or IPVS-mode CNIs still need the explicit mode change.
- Forgetting that managed providers gate version availability separately from open-source releases. Planning a migration date before confirming EKS, AKS, or GKE actually expose 1.37 wastes a maintenance window.
- Leaving etcd on an old minor version. The metrics.k8s.io v1 promotion and other 1.37 features assume etcd 3.5.16 or later; an outdated etcd can cause subtle API registration failures that don’t show up until under load.
- Not testing CronJobs and scheduled scaling events in staging. Time-triggered workloads are the most common source of “it worked fine for two days, then broke” upgrade failures.
- Relying on a single local etcd snapshot. If the node holding your only backup is also affected by the incident that forces a rollback, you have no recovery path.
Expected Output at Each Stage
Here’s what a clean upgrade looks like at the command line, so you can compare against your own output and catch problems early.
$ kubeadm upgrade plan
[upgrade/versions] Cluster version: v1.36.4
[upgrade/versions] kubeadm version: v1.37.0
Upgrade to the latest stable version:
COMPONENT CURRENT TARGET
kube-apiserver v1.36.4 v1.37.0
kube-controller-manager v1.36.4 v1.37.0
kube-scheduler v1.36.4 v1.37.0
kube-proxy v1.36.4 v1.37.0
$ kubectl get nodes -o custom-columns=NAME:.metadata.name,VERSION:.status.nodeInfo.kubeletVersion
NAME VERSION
control-plane-1 v1.37.0
control-plane-2 v1.37.0
control-plane-3 v1.37.0
worker-node-01 v1.37.0
worker-node-02 v1.37.0
$ kubectl top nodes
NAME CPU(cores) CPU% MEMORY(bytes) MEMORY%
control-plane-1 412m 10% 2145Mi 27%
worker-node-01 891m 22% 3872Mi 48%
If kubectl top nodes returns “error: Metrics API not available” after upgrade, that’s your signal to revisit Step 10 and confirm metrics-server is running the version that registers against the stable metrics.k8s.io v1 API group.
Troubleshooting Guide
These are the specific failure modes teams have reported during 1.37 migrations, along with the fix for each.
- Static pods stuck in CrashLoopBackOff after upgrade: almost always the removed static pod secret support from Step 2. Check kubelet logs on the affected node (
journalctl -u kubelet -f) for a message referencing an unsupported volume source, then convert the manifest to hostPath per Step 9. - kube-proxy pods failing to start with an IPVS-related error: the IPVS kernel modules may be missing on newly provisioned nodes running a minimal OS image. Either load the modules explicitly or migrate off IPVS mode entirely as described in Step 9.
- HPA stuck showing “unknown” for current metrics: the metrics-server deployment is likely still serving only v1beta1. Redeploy metrics-server at a version that registers v1, and confirm with
kubectl get apiservices | grep metrics. - Nodes stuck in NotReady after kubelet restart: check for a containerd or CRI-O version mismatch. 1.37 kubelet expects a CRI implementation that supports the current CRI API version; an outdated container runtime is the usual culprit.
- kubectl commands returning “the server could not find the requested resource”: your kubectl client is more than one minor version behind or ahead of the API server. Update the client to match Step 1’s skew requirements.
- Pods failing to mount volumes on SELinux-enforcing RHEL nodes: SELinuxMount’s default-on behavior in 1.37 changes label application timing. Check
audit.logon the affected node for AVC denials and adjust custom SELinux contexts if your workload set them manually. - etcd snapshot restore fails with a version mismatch error: etcd snapshots are generally not cross-version compatible beyond a minor version or two. Always restore to a cluster running the same etcd version that produced the snapshot, then upgrade forward from there if needed.
- Managed node group upgrade stuck “in progress” for over an hour: check for pod disruption budgets (PDBs) that are blocking node drain. A PDB requiring 100% availability on a single-replica deployment will stall the entire surge upgrade indefinitely.
Advanced Tips for Large Fleets
If you’re managing dozens of clusters rather than one, a few practices reduce the operational burden significantly. First, automate the version-skew and static-pod audits from Steps 1 and 2 into a scheduled check that runs against every cluster continuously, not just before a planned upgrade — that way you catch drift the moment it appears rather than discovering it during a maintenance window. Second, stagger upgrades across clusters by risk tier: upgrade your lowest-traffic, most-observable cluster first, let it run for a full business cycle (at least one week, including any weekly batch jobs), then proceed to higher-tier clusters only after that soak period is clean.
Third, pin your CI/CD pipeline’s kubectl version to match your oldest supported cluster, not your newest, to avoid the skew errors described in the troubleshooting section above. Fourth, for organizations running GitOps tooling like Argo CD or Flux, add a pre-sync hook that checks cluster API server version against manifest apiVersion requirements, catching metrics.k8s.io v1beta1 references and similar issues automatically before they reach production. Finally, keep a rolling three-version compatibility matrix in your internal documentation (current minus two, current minus one, current) so any engineer can quickly see what’s safe to deploy against which cluster tier without checking release notes each time.
Complete Working Example: A Full Migration Script
Below is a consolidated script that ties the individual steps together for a self-managed kubeadm cluster. Adapt the package manager commands and paths to your distribution, and run it interactively, node by node, rather than as an unattended batch job.
#!/bin/bash
set -euo pipefail
TARGET_VERSION="1.37.0"
BACKUP_DIR="/backup/k8s-137-migration"
mkdir -p "$BACKUP_DIR"
echo "=== Step 1: Pre-flight audit ==="
kubectl get nodes -o custom-columns=NAME:.metadata.name,VERSION:.status.nodeInfo.kubeletVersion
grep -A3 "secret:" /etc/kubernetes/manifests/*.yaml || echo "No static pod secrets found"
kubectl get configmap kube-proxy -n kube-system -o yaml | grep mode || true
echo "=== Step 2: Backup etcd ==="
ETCDCTL_API=3 etcdctl --endpoints=https://127.0.0.1:2379 \
--cacert=/etc/kubernetes/pki/etcd/ca.crt \
--cert=/etc/kubernetes/pki/etcd/server.crt \
--key=/etc/kubernetes/pki/etcd/server.key \
snapshot save "$BACKUP_DIR/etcd-snapshot-$(date +%Y%m%d-%H%M).db"
echo "=== Step 3: Upgrade kubeadm and plan ==="
apt-get update && apt-get install -y "kubeadm=${TARGET_VERSION}-1.1"
kubeadm upgrade plan
read -p "Review the plan above. Proceed with upgrade? (yes/no) " confirm
if [ "$confirm" != "yes" ]; then
echo "Aborted by operator."
exit 1
fi
echo "=== Step 4: Apply upgrade ==="
kubeadm upgrade apply "v${TARGET_VERSION}" -y
echo "=== Step 5: Upgrade kubelet and kubectl ==="
apt-get install -y "kubelet=${TARGET_VERSION}-1.1" "kubectl=${TARGET_VERSION}-1.1"
systemctl daemon-reload
systemctl restart kubelet
echo "=== Step 6: Post-upgrade validation ==="
sleep 30
kubectl get nodes
kubectl get pods -n kube-system
kubectl top nodes || echo "WARNING: metrics API not responding, check metrics-server"
echo "=== Migration to ${TARGET_VERSION} complete on this node ==="
Run this script on the first control-plane node, confirm the validation output looks correct, then repeat the equivalent (with kubeadm upgrade node instead of apply) on each remaining control-plane node before moving on to worker nodes with the surge pattern from Step 8.
How Long Should the Full Migration Take?
For a small cluster (three control-plane nodes, under 20 workers), budget a full day: a few hours for the staging run and static-pod audit, followed by a maintenance window of one to two hours for the actual production upgrade, plus a monitoring period afterward. For larger fleets with dozens of node pools across multiple clusters, plan the rollout over one to two weeks using the staggered, risk-tiered approach described in the advanced tips section, rather than attempting every cluster in a single window. The time-consuming part is rarely the upgrade commands themselves; it’s the static pod manifest audit and staging validation that determine whether the actual cutover goes smoothly.
Frequently Asked Questions
Can I upgrade directly from Kubernetes 1.34 to 1.37?
No. Kubernetes only officially supports upgrading one minor version at a time, so a 1.34 cluster needs to pass through 1.35 and 1.36 before reaching 1.37. Skipping versions can leave the API server unable to correctly process objects created under intermediate schema changes.
Is Kubernetes 1.37 available yet on EKS, AKS, and GKE?
Availability depends on each provider’s own rollout schedule, which typically trails the open-source release by several weeks. Check aws eks describe-addon-versions, az aks get-upgrades, or the GKE server config API directly for your cluster’s region before planning a migration date.
Will the IPVS deprecation break my cluster immediately?
No. Deprecated in 1.37 means it still functions in this release but is marked for eventual removal in a future version. You have time to migrate to nftables mode or an eBPF-based CNI, but starting the migration now avoids a forced, rushed change later.
What happens to static pods that reference Secrets after upgrading to 1.37?
They will fail to start, since static pod secret volume support is removed in this release. You must convert them to reference host-mounted file paths via hostPath volumes before upgrading, or the affected control-plane components (commonly etcd, kube-apiserver) will not come back up.
Do I need to upgrade etcd separately from the control plane?
Yes, in most kubeadm-managed setups etcd runs as a static pod alongside the control plane and gets upgraded as part of the same kubeadm upgrade apply process, but you should independently confirm your etcd version is 3.5.16 or later, since some long-lived clusters carry an older etcd version that was never explicitly bumped.
Can I roll back a control-plane upgrade if something goes wrong?
Kubernetes does not support an in-place minor-version downgrade of the control plane. Your practical rollback path is restoring the etcd snapshot taken before the upgrade onto freshly provisioned nodes running the prior version, which is why that backup step is non-negotiable.
How do I know if my workloads are affected by the SELinuxMount change?
It only matters on nodes with SELinux in enforcing mode, typically RHEL, Rocky Linux, or AlmaLinux hosts. Clusters running Ubuntu, Debian, or other distributions without SELinux enforcement are unaffected. Check node OS images with kubectl get nodes -o custom-columns=NAME:.metadata.name,OS:.status.nodeInfo.osImage.
What’s the safest order to upgrade a multi-cluster environment?
Start with your lowest-traffic, most heavily monitored cluster and let it run for a full business cycle before touching higher-tier clusters. This surfaces time-triggered failures, like CronJob or scheduled-scaling issues, before they can affect a production-critical cluster.


