Skip to main content

Kubernetes v1.37's Deprecations Are a Ticking Clock: Audit Your Self-Hosted PaaS Before the Upgrade Window Closes

9 min readDora NodaDora Noda
Share
On this page

Kubernetes v1.37 "Garhwal" shipped on August 26, 2026 with 67 enhancements — 16 graduated to stable, 23 to beta, 27 entering alpha, and one deprecation. The headline features got the attention: gang scheduling in beta, HPA scale-to-zero, DRA extended resources going GA. But buried in the release notes is the part that will actually page somebody at 3 a.m. next year: a cleanup list with hard deadlines.

Three legacy pillars of every cluster — kube-dns, kube-proxy's IPVS mode, and cgroup v1 — now have dated removal timelines attached. Nothing breaks today. That is exactly why teams ignore it, and exactly why the teams running their own control planes should not.

Anyone who operated clusters through the v1.22 cycle remembers how this story ends. The networking.k8s.io/v1beta1 Ingress API vanished, and every cluster that had skipped the deprecation warnings learned about it from failing deploys instead of from the changelog. Beta APIs graduate or disappear; that has always been the deal. The only question is whether you find out from an audit or from an outage.

The v1.37 sunset table: three deadlines, no surprises if you read them now

Here is the entire cleanup list that matters for your next upgrade window, in one place:

SunsettingReplacementDeadlineWhat breaks if you miss it
kube-dns add-onCoreDNSRemoval expected by v1.40Nodes boot with no working cluster DNS; every service lookup fails
kube-proxy ipvs mode (KEP-5495)nftables backendDisabled by default in v1.40, removed entirely in v1.43kube-proxy refuses to start in the configured mode; pod networking never programs
cgroup v1 worker nodescgroup v2kubelet already refuses v1-only nodes since v1.35 (temporary workaround excepted)New or re-imaged nodes never join the cluster; capacity silently stops growing

A few clarifications so nobody over- or under-reacts. First, the v1.37 release itself removes almost nothing — the official count is a single deprecation. The danger is not upgrading to 1.37; it is sleepwalking through 1.37 toward 1.40 with the old components still configured. Second, the GA graduations in this release (StorageVersionMigration, the Metrics API, KYAML output for kubectl) are good news, not threats — but they change operational defaults, so they belong in your upgrade notes, not your audit failures. Third, clusters that never explicitly configured a kube-proxy mode already receive deprecation warnings as of v1.37 (KEP-5343). If your logs are showing those warnings, the clock is already ticking for you specifically.

Why a self-hosted PaaS control plane is more exposed than its workloads

On a managed cluster, two of these three rows are your provider's problem. The managed control plane migrates DNS, the node images ship cgroup v2 by default, and the kube-proxy mode is whatever the provider validated. You might still run a stale Ingress manifest, but the platform absorbs the infrastructure half of the migration.

On a Cluster-API-managed fleet, you own all of it — and the exposure concentrates in four places that tenant-workload audits never look at:

MachineTemplates pin your node OS and kube-proxy mode. The cgroup version is a property of the machine image your KubeadmConfigTemplate and node bootstrap produce, not of any workload manifest. A fleet whose image pipeline still stamps out cgroup v1 nodes will discover it the way every capacity problem announces itself: new machines stop joining during a scale-up, at the worst possible moment. Similarly, if your cluster templates ever set kube-proxy to IPVS mode explicitly — common on fleets tuned years ago for large Service counts — that string is sitting in version-controlled YAML today, counting down to v1.43.

Your own controllers speak APIs that move under them. A platform that provisions custom domains, per-tenant quotas, or build queues with bespoke controllers (cheap to write on modern kubebuilder, per this summer's controller-runtime deep dive) also inherits the API versions those controllers were generated against. A controller built against a beta API that graduates can keep running — but one built against a removed version fails closed, and it fails in the reconcile loop, which means it fails repeatedly, loudly, and across every tenant at once.

CRDs carry stored versions that outlive your manifests. Even after every YAML file in git points at the new API, etcd may still store objects at the old version until a storage migration runs. This is precisely what the newly-GA StorageVersionMigration API exists to fix — v1.37 is a good release to finally use it rather than admire it.

Admission webhooks accept or reject based on versions they understand. A validating webhook that only knows the old shape of an object becomes a deny-all firewall the day the stored version moves past it. Webhooks are the quietest member of this list and historically the one discovered last.

The audit: six checks, all runnable before your next upgrade

Run these against a mirror of production — never for the first time against production — during the pre-upgrade window. Each check maps to one row of the sunset table or one exposure above.

1. Scan every manifest and Helm release for removed APIs with kubent.

bash
kubent

kube-no-trouble reads your cluster (or a directory of manifests) and flags resources stored at API versions that are deprecated or removed in your target Kubernetes version. Point it at the target version you plan to upgrade to, not the one you run today — auditing against 1.37 when you are about to jump to 1.38 answers last month's question.

2. Ask the API server who is still touching deprecated APIs.

bash
kubectl get apirequestcounts -o json | jq '.items[] | select(.status.removedInRelease != null)'

APIRequestCount is the server-side record of which clients called which API versions in the last 30 days. Manifest scans find what is stored; this finds what is called — including that one-off controller or cron job whose YAML nobody can find anymore. Any client still calling an API with a removedInRelease set is a deployment-day failure with a name attached.

3. Read your kube-proxy mode out of the live cluster config.

bash
kubectl -n kube-system get configmap kube-proxy -o jsonpath='{.data.config\.conf}' | grep 'mode:'

If the answer is ipvs, you have until v1.40 before the default flips under you and until v1.43 before the option disappears. Plan the migration to nftables as its own change, tested on a canary node pool — not as a footnote to a version bump. If the field is empty, you are on defaults and only owe yourself a re-check per release.

4. Verify every node pool is actually on cgroup v2.

bash
kubectl get nodes -o json | jq -r '.items[] | [.metadata.name, .status.nodeInfo.osImage, .status.nodeInfo.kernelVersion] | @tsv'

Cross-reference the OS images against your image pipeline: any template still producing cgroup v1 images must be rebuilt and rolled before the temporary workaround expires. Remember that since v1.35 the kubelet refuses to initialize v1-only nodes, so this check fails closed — a stale image does not degrade, it simply never joins.

5. Confirm CoreDNS, and delete the kube-dns assumption from bootstrap.

bash
kubectl -n kube-system get pods -l k8s-app=kube-dns -o name

If that returns pods, you are running the add-on slated for removal by v1.40 — migrate to CoreDNS now, while DNS is healthy and you can compare resolvers side by side. If it returns nothing, grep your node-bootstrap and cluster templates for kube-dns references anyway; the add-on has a way of surviving in install scripts long after the cluster moved on.

6. Check CRD stored versions, then migrate them.

bash
kubectl get crds -o json | jq -r '.items[] | [.metadata.name, (.status.storedVersions | join(","))] | @tsv'

Any CRD whose stored version is not the newest served version is carrying migration debt. With StorageVersionMigration now GA in v1.37, there is a supported, declarative path to drain that debt — run it per CRD in staging first, watching your own controllers' and webhooks' logs for version-skew errors as each migration lands.

Time the audit to your upgrade window, not the release headlines

The v1.37 cycle itself demonstrates the mechanism worth copying: enhancements froze June 17, code froze July 23, and GA landed August 26. That code-freeze-to-GA stretch — roughly five weeks where the removal list is final but nobody's cluster is affected yet — is the audit window for every release, and it recurs like clockwork three times a year.

So do not file this post under "1.37 news." Translate it into your own calendar:

  • Name your next target version now (the 1.36-to-1.37 jump, the 1.37-to-1.38 jump, whichever is yours) and its code-freeze date. That date, not GA day, is your audit deadline.
  • Run all six checks once per cycle, in staging, between code freeze and GA. The nearest concrete window after publication is the v1.37.1 patch cycle closing mid-September — a small, safe rehearsal for the full audit habit.
  • Treat warnings as tickets. A deprecation warning in the apiserver log or a removedInRelease in APIRequestCount is a dated work item with a known assignee (whoever owns that controller, template, or webhook), not background noise. File it the week it appears, when the fix is a migration — not the week the API disappears, when the fix is an incident.

Fleets that do this spend one calm afternoon per release. Fleets that do not spend one very long night per removal, relearning the v1.22 Ingress lesson with different API group names.

The recurring cost nobody puts on the invoice

This is the maintenance item that never appears in a self-hosting cost comparison and always appears in the on-call rotation. Auditing deprecations, migrating stored versions, re-validating node images, re-testing the kube-proxy backend — none of it ships a feature, all of it prevents an outage, and a managed-Kubernetes customer never sees any of it because their provider's platform team does it for every tenant at once.

Owning the machines means owning the calendar too. The v1.37 sunset table gives you roughly three releases of runway on kube-dns and IPVS, and no runway at all on cgroup v1. Spend the calm afternoon.

Bex.co is the open-source, AI-native Render alternative — push a git repo, get a running HTTPS service on machines you own. Star the repo on GitHub or deploy your first app today.

Related articles

Run this on infrastructure you own

bex is the open-source, AI-native Render alternative — push a git repo and get a running HTTPS service on your own machines.

Get started with bex