Kubernetes 1.37: The Security Wins and the Deprecations That Break Upgrades

Share
Kubernetes 1.37: The Security Wins and the Deprecations That Break Upgrades
A Kubernetes cluster upgrade checklist flagging ipvs, cgroup v1, and containerd deprecations before v1.37.

Kubernetes 1.37 arrives in August 2026, and the release-team sneak peek reads as a feature list. Sorted by what actually affects a running cluster, it splits cleanly into two piles: a couple of genuine security wins worth adopting, and a set of long-signaled deprecations that will stop an unprepared cluster from upgrading. This rundown leads with the second pile, because a broken upgrade is a worse Monday than a missed feature, then covers the hardening that's worth your attention. Every deprecation below comes with the one-liner to check whether your own cluster is exposed.

What Will Break Your Upgrade

Three long-running deprecations converge around this release, and each has bitten clusters that assumed "deprecated" meant "still fine for now."

cgroup v1: the kubelet won't start

Since v1.35, failCgroupV1 defaults to true, meaning the kubelet refuses to initialize on any node still running cgroup v1 unless you explicitly override it. In 1.37 that override still exists but it's a stopgap with a shrinking runway:

apiVersion: kubelet.config.k8s.io/v1beta1
kind: KubeletConfiguration
failCgroupV1: false  # temporary override, do not rely on this

⚠️ Treat that override as a countdown, not a fix. cgroup v1 removal is planned outright in a future release, and the modern resource features (In-Place Pod Resizing, Tiered Memory Protection) depend entirely on v2, so a node pinned to v1 is frozen out of everything new. Check every node before you upgrade:

kubectl get nodes -o name | while read n; do node="${n#node/}"; echo -n "$node: "; kubectl debug "$n" -it --image=busybox -- stat -fc %T /sys/fs/cgroup/ 2>/dev/null | tail -1; done

Or straight on the node, which is faster if you have SSH:

stat -fc %T /sys/fs/cgroup/

cgroup2fs means v2 (good). tmpfs means you're still on v1 (hybrid or legacy) and that node will block the kubelet once the override goes away. Modern distros default to v2; the ones that bite are older LTS installs, custom kernels, and hosts that never finished the migration.

containerd 1.x: no longer supported

This one the release-team post doesn't emphasize, and it's a hard blocker: containerd 1.x support is dropped in this cycle, containerd 2.0 is the minimum. It's aligned with containerd 1.7 EOL and has been signaled since 1.35, but "signaled" and "handled" are different states on most clusters. Check every node:

kubectl get nodes -o wide

The CONTAINER-RUNTIME column shows the version (e.g. containerd://1.7.20). Anything on 1.x needs upgrading to 2.0+ before the cluster moves to 1.37, or the node's runtime falls out of support. Watch the kubelet_cri_losing_support metric as your early-warning signal.

ipvs mode: deprecated, warning on startup

kube-proxy's ipvs mode is deprecated since 1.35, and in 1.37 kube-proxy logs a deprecation warning on startup if you're running it. The rationale is honest: ipvs mode never fully replaced iptables (it still calls iptables underneath for the parts the kernel ipvs API can't express), so it's technical debt with no payoff over the newer nftables backend, which has been GA since 1.33. The timeline:

  • 1.40: ipvs mode disabled by default (still selectable via feature gate)
  • 1.43: ipvs mode removed entirely

Check which mode you're running:

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

⚠️ If that returns mode: ipvs, you have runway (removal isn't until 1.43) but no reason to wait. On modern kernels, nftables is the recommended backend and the migration is a kube-proxy config change plus a rollout. Plan it now while it's a scheduled task rather than later when it's a forced one. This is also a good moment to review your NetworkPolicy enforcement, since the proxy backend and your network policies are the two halves of how service traffic actually flows.

The Breaking Change That Isn't a Deprecation: SELinuxMount GA

⚠️ This one graduates to GA and enabled-by-default in 1.37, and it can stop pods from starting on SELinux-enforcing clusters, so it's worth understanding before you upgrade even though nothing is being removed.

Previously, volumes with SELinux got recursively relabeled to match a pod's context. Now, when a CSI driver opts in (CSIDriver with .spec.seLinuxMount: true), the volume is mounted with -o context=<label> instead of relabeled. The catch: a single mount can hold only one SELinux context, so two pods with different SELinux labels sharing a volume on the same node, which used to coexist under recursive relabeling, may now fail to start.

If you have workloads sharing a volume across pods with differing labels, restore the old behavior per-workload in the pod spec:

spec:
  securityContext:
    seLinuxChangePolicy: Recursive

Clusters without SELinux enabled see no effect at all. If you don't know whether yours enforces SELinux, that's the thing to confirm before upgrading, not after a pod fails to schedule.

The Security Wins Worth Adopting

Now the good news, and it's genuinely good if you care about node-level blast radius.

Kubelet in User Namespace (Rootless Mode) to Beta

This is the standout. Traditionally the kubelet and other node components run as root on the host, so a vulnerability in one of them is a host-level compromise. In 1.37, kubelet-in-userns graduates to Beta: node components run inside a Linux user namespace as an unprivileged host user while still behaving as root inside the namespace.

The security payoff is direct and it's the same principle behind every container-escape mitigation: reduce what a compromised component can reach. A kubelet exploit on a rootless node lands as an unprivileged host user rather than root, which caps the damage sharply. This is the node-daemon version of the argument I made for verifying container isolation, where the shared host kernel is the real boundary and running privileged is the thing that makes an escape catastrophic. Beta means it's not the default yet, but it's the direction node security is heading, and worth piloting on non-critical node pools now.

Static Pods Can No Longer Reference Secrets or ConfigMaps

A quieter hardening change with a clean rationale. Static pods (managed by the kubelet directly from a manifest file, not through the API server) were never supposed to read API resources, they exist precisely for the bootstrap case where the API server isn't available. A bug let them reference Secrets and ConfigMaps via configMapRef/secretRef anyway. In 1.37 that's fixed and strictly prohibited, and the PreventStaticPodAPIReferences feature gate that let you opt out is removed.

⚠️ If any of your static pod manifests (typically under /etc/kubernetes/manifests/ on control-plane nodes) reference a Secret or ConfigMap, they'll break on upgrade. Check before you go:

grep -rlE 'configMapRef|secretRef|configMapKeyRef|secretKeyRef' /etc/kubernetes/manifests/ 2>/dev/null

Any hit needs the values inlined into the manifest or the workload moved to a regular API-managed pod before upgrading. This closes a genuine trust-boundary oddity (a component that runs without API authz shouldn't be pulling API secrets), which fits the broader "authorization is not the same as containment" theme running through the CSI path-traversal lesson.

The Feature Everyone Will Cite: Metrics API GA

metrics.k8s.io finally graduates to Stable after nearly nine years in Beta. It's the API behind kubectl top and the Horizontal Pod Autoscaler, so this is recognition of something you already depend on rather than a functional change, no behavior difference expected, and both v1 and v1beta1 stay usable through the transition. Nothing to do here except stop feeling nervous that a load-bearing API was still Beta.

Also Worth Knowing

  • Volume Health Monitor resets to Alpha with four new CSI RPCs, giving CSI drivers a machine-readable way to report storage failures (Inaccessible, Degraded, etc.) into PersistentVolumeClaim.status.healthStatus and Pod.status.volumeHealth, instead of failures surfacing only as hung mounts you cross-reference against a vendor dashboard. Alpha, so not for production reliance yet, but the right direction for storage observability. Pairs well with the CIS-benchmark-driven cluster monitoring approach.
  • kubectl run --filename/-f deprecated, since the generated pod is always built purely from CLI arguments. Minor, but if you script kubectl run -f, update it.

Bottom Line

Before you upgrade to 1.37, run the four checks: cgroup version on every node (stat -fc %T /sys/fs/cgroup/), containerd version (kubectl get nodes -o wide), kube-proxy mode (the configmap grep), and static-pod manifests for Secret/ConfigMap references. Those are the things that turn an upgrade into an incident. Then, on the adoption side, pilot rootless kubelet on a non-critical node pool, because running node daemons as an unprivileged host user is the single biggest blast-radius reduction in this release. The metrics API going GA is nice, but the deprecations are what will actually change your Monday, so check them first.


References