Kubernetes v1.37 promoted Memory QoS to Beta on September 14, and the MemoryQoS feature gate is now on by default on every v1.37 node running cgroup v2. That means the kernel finally knows how to protect your pods’ memory — not just the scheduler. If OOM-killed pods have become accepted background noise in your cluster, this changes the math. But there is one default change that will catch you off guard if you skip the release notes.
The Default That Silently Changed
Here is the thing most upgrade guides will miss. In earlier versions with the feature gate enabled, memoryThrottlingFactor defaulted to 0.9 — the kubelet would automatically set memory.high to 90% of a container’s memory limit. In v1.37, that default flipped to null.
So even though the MemoryQoS feature gate is now on by default, memory.high is not set automatically unless you opt in. This was deliberate: silently throttling workloads on upgrade would break clusters that never asked for it. The right call — but you need to know it happened.
The rule: if your kubelet config already had an explicit memoryThrottlingFactor value, that value is preserved. If it did not, you need to add one to get throttling behavior.
What Memory QoS Actually Does
Memory QoS maps Kubernetes QoS classes to four cgroup v2 memory controller interfaces. Before this feature, “Guaranteed” in Kubernetes meant the scheduler respected your memory request, but the kernel’s OOM killer did not — it would still kill your pod if the node ran out of memory. Memory QoS changes that at the kernel level.
- memory.min — Guaranteed pods: a hard memory floor. The kernel will not reclaim this memory under any circumstances. If it cannot honor the guarantee, it invokes the OOM killer on other processes first.
- memory.low — Burstable pods: a soft floor. The kernel prefers to retain this memory but may reclaim it under severe node pressure.
- memory.high — Burstable and BestEffort containers: a throttle threshold. The kernel starts compaction and reclaim when a container crosses this line, giving the application a chance to release memory rather than being killed outright.
- memory.max — the hard limit, unchanged from before.
The difference between throttled and killed is significant in practice. When a container crosses memory.high, the kernel slows memory allocation — a Java app gets the signal to run GC, a Python process stops getting new allocations easily. The application slows. It does not die. That window is what kubelet uses to decide on graceful eviction rather than abrupt termination.
How to Enable the Full Stack
Two lines in your kubelet configuration cover both memory protection and throttling:
apiVersion: kubelet.config.k8s.io/v1beta1
kind: KubeletConfiguration
memoryThrottlingFactor: 0.9
memoryReservationPolicy: TieredReservation
TieredReservation activates memory.min for Guaranteed pods and memory.low for Burstable pods. memoryThrottlingFactor: 0.9 sets memory.high at 90% of the memory limit for Burstable and BestEffort containers. You want both. Without TieredReservation, you get throttling but no hard protection. Without memoryThrottlingFactor, you get protection but no gradual slowdown before the limit is hit.
Before You Enable It: Check Your Nodes
Memory QoS is a no-op on cgroup v1 nodes. Requirements:
- Linux kernel 5.8 or later (5.9+ to avoid potential livelocks at
memory.high) - cgroup v2 active on nodes
- containerd 1.6+ or CRI-O 1.22+
Most managed cluster defaults already satisfy this. EKS on Amazon Linux 2023 uses cgroup v2 by default. GKE’s Container-Optimized OS has shipped with cgroup v2 since GKE 1.26. Confirm with:
cat /sys/fs/cgroup/cgroup.controllers
# Output should include: memory
If memory does not appear in that output, the memory controller is not active and Memory QoS cannot set any values. Check your node cgroup configuration before proceeding. If you upgraded to Kubernetes 1.37 and have not yet moved to cgroup v2, our Kubernetes v1.37 upgrade guide covering containerd 2.0 and cgroup v2 covers the prerequisites.
Where This Makes the Biggest Difference
Three categories of workloads stand to benefit most:
JVM applications are the clearest win. The JVM heap grows right up to the container limit before GC decides to collect. Without memory.high, the kernel OOM-kills the container before GC runs. With throttling at 90%, the JVM feels memory pressure earlier and collects — slow but alive.
AI inference servers spike memory during requests and need hard floors. A model server configured as Guaranteed with memory.min set to its request will not have memory reclaimed mid-inference. On a heavily loaded node, that matters considerably.
Multi-tenant clusters benefit from Burstable pods getting memory.low protection. A noisy neighbor that suddenly demands memory is less likely to trigger eviction of well-behaved pods sitting at their request levels.
A 2026 Fairwinds survey found OOM kills in 31% of production Kubernetes clusters. Memory QoS does not eliminate OOM kills — it does not override hard limits — but it replaces sudden deaths with throttled slowdowns for the majority of cases where a pod is not misconfigured, just occasionally spiky.
Upgrade Checklist
- Verify cgroup v2 is active on your nodes (
cat /sys/fs/cgroup/cgroup.controllers) - Check whether your kubelet config had an explicit
memoryThrottlingFactor— if not, the feature gate being on does not give you throttling - Add
memoryThrottlingFactor: 0.9andmemoryReservationPolicy: TieredReservationto your kubelet config - Roll kubelet config changes to nodes one at a time and monitor memory metrics
- Watch for the kubelet warning if your kernel is older than 5.9
The full details are in the official Memory QoS Beta announcement. The v1.36 TieredReservation post has context on how the protection tiers were designed.













