Kubernetes Swap Support: Should You Turn It On in 1.35?
Kubernetes swap went stable in 1.35. Here's how it actually works, when it helps, when it hurts, and how to configure LimitedSwap correctly.
On this page
Short answer: enable it per node with failSwapOn: false and memorySwap.swapBehavior: LimitedSwap in the kubelet config, and only your Burstable pods will actually use it. Guaranteed pods are excluded by design. It's a genuine win for memory-spiky, cache-heavy workloads and a bad idea for latency-sensitive request handlers.
For most of Kubernetes' existence, running with swap enabled on a node simply wasn't allowed. The kubelet would refuse to start. That made sense at the time: Kubernetes builds its scheduling and eviction decisions on the assumption that a byte of RAM a pod is granted behaves like RAM, fast and predictable, and swap quietly breaks that assumption. A pod that gets OOMKilled at 90% memory headroom because a brief spike tripped the node's eviction threshold isn't a bug. It's the system working as designed, just not in a way that feels forgiving.
What Actually Changed in 1.34 and 1.35
Node swap support, tracked as KEP-2400, went GA in Kubernetes 1.34 and reached stable in 1.35, which shipped as the "Timbernetes" release. If you're running a self-managed cluster (kubeadm, bare metal, or cloud VMs where you control node config, as opposed to a fully managed node pool where this setting isn't exposed to you) and you've been putting off a decision on this, this is the release where teams are actually flipping it on.
It's worth being precise about what "stable" means here, because it's easy to read that word as "turned on for you." It isn't. Swap support is opt-in at two separate levels: the node has to have swap space actually provisioned on disk, and the kubelet has to be explicitly configured to allow pods to use it. Nothing changes on an existing cluster just because you upgraded to 1.35. The kubelet still refuses to start on a swap-enabled node unless you tell it otherwise.
The QoS Class Restriction That Surprises People
The detail that trips people up first is that swap access isn't a node-wide switch. It's gated by pod QoS class, and only Burstable pods (the ones where at least one container sets a memory request lower than its limit) get access to swap at all. BestEffort pods, which set no requests or limits, are also excluded. Guaranteed pods, where every container's memory request exactly equals its limit, never touch swap no matter what you configure on the node.
That's not an oversight or a current limitation waiting to be lifted in a future release. It's the point. Choosing Guaranteed QoS is an explicit statement that a workload needs dedicated, predictable memory with no surprises, and letting it silently swap would undermine the entire reason someone reached for that QoS class in the first place. If you're not already comfortable with how Kubernetes derives QoS class from requests and limits, it's worth reviewing the mechanics before you touch swap config, since the swap behavior you get is entirely downstream of a decision you already made when you wrote your pod spec. Our Kubernetes interview questions guide covers the QoS class breakdown (Guaranteed, Burstable, BestEffort) and how the scheduler and eviction manager treat each one differently.
Prerequisites: cgroup v2 and Your Container Runtime
Swap support only works on nodes running cgroup v2. If your nodes are still on cgroup v1 (increasingly rare on current distros, but worth checking if you're on an older base image), the kubelet will reject the swap configuration outright rather than silently ignoring it. Confirm which version a node is running before you do anything else:
stat -fc %T /sys/fs/cgroup/
# "cgroup2fs" means you're on cgroup v2 and clear to proceed
# "tmpfs" means you're still on cgroup v1 (hybrid or legacy mode)You'll also need a container runtime that supports cgroup v2 memory swap limits, which means a current containerd or CRI-O build. Most distributions that ship cgroup v2 by default (recent Ubuntu, Fedora, and their derivatives) already pair it with a runtime version new enough to work, but it's worth confirming on any node image you built or pinned a while ago rather than assuming.
Turning It On, Correctly
First, provision swap on the node itself. Kubernetes doesn't create this for you; it just changes how the kubelet reacts once swap exists:
sudo fallocate -l 4G /swapfile
sudo chmod 600 /swapfile
sudo mkswap /swapfile
sudo swapon /swapfile
echo '/swapfile none swap sw 0 0' | sudo tee -a /etc/fstabThen configure the kubelet to allow it:
apiVersion: kubelet.config.k8s.io/v1beta1
kind: KubeletConfiguration
failSwapOn: false
memorySwap:
swapBehavior: LimitedSwapThe swapBehavior value matters more than it looks like it should. NoSwap is the default once failSwapOn is set to false: the kubelet will start on a swap-enabled node, but your pods still won't touch that swap. That combination, swap provisioned on disk but NoSwap configured, is a trap people fall into constantly. You've spent disk space and gotten zero behavioral change, and you'll spend real debugging time later wondering why memory pressure is still evicting pods exactly like before.
LimitedSwap is what you actually want if the goal is giving Burstable pods real swap access. Under LimitedSwap, a Burstable container's swap usage is capped in proportion to how much its memory request sits below its limit, so a pod can't quietly swap its way past the memory ceiling the scheduler placed it against. Guaranteed and BestEffort pods still get nothing, regardless of this setting.
Restart the kubelet after applying the config change, and give it a minute to re-register the node with its updated capacity before you schedule anything that depends on the new behavior.
sudo systemctl restart kubelet
kubectl get node <node-name> -o jsonpath='{.status.allocatable.memory}'Verifying It Actually Worked
Don't take the config file's word for it. Confirm the node actually sees swap and the kubelet started with it enabled:
# Confirm swap is provisioned and active on the node
free -h
# Confirm the kubelet didn't silently fall back to refusing swap
journalctl -u kubelet | grep -i swapThen deploy a small Burstable test pod (a request below its limit) that deliberately allocates memory past its request, and watch free -h on the node while it runs. If swap usage climbs instead of the pod immediately hitting eviction or an OOM kill, the configuration is working end to end, not just accepted by the kubelet on paper.
When This Genuinely Helps
The clearest win is a workload with a large memory footprint but a small active working set: a service holding a big in-memory cache or dataset where only a fraction of it is touched at any given moment. The rarely-accessed pages can sit in swap without meaningfully affecting performance, and the node avoids evicting or killing a pod that looks like it's using more memory than it actually needs moment to moment.
Batch and background jobs that can tolerate some slowdown are the other good fit. A nightly data processing job that swaps a bit and takes twenty-two minutes instead of eighteen is a fine trade against getting OOMKilled partway through and restarting from scratch, especially for jobs that aren't cleanly idempotent or checkpointed.
When This Makes Things Worse
Latency-sensitive request-serving services are the wrong fit, full stop. A memory access that normally costs nanoseconds becomes a disk read measured in milliseconds the moment that page is swapped out, and that shows up as p99 latency spikes that are genuinely harder to diagnose than an OOM kill. An OOM kill is loud: the pod restarts, your alerting fires on the restart count, and you know immediately what happened and when. A swap-thrashing pod just gets quietly slower in a way that looks like a mystery performance regression, and it can stay that way for a long time before anyone thinks to check node-level swap usage instead of application code.
There's a real debugging cost buried in that failure mode too. Most profiling and metrics tooling assumes memory access cost is roughly uniform, and that assumption starts lying to you once swap enters the picture. If you're already fighting production memory issues that don't show up as clean crashes, that pattern will feel familiar. We covered the same shape of problem from the application side in our guide to catching Go goroutine leaks before they OOMKill a pod, where the failure is silent right up until a node-level threshold gets crossed. If you're trying to work backward from a crash that's already happened, PodTriage walks through the kubectl describe and events output needed to tell an OOMKilled pod apart from a node-level eviction, which is exactly the distinction that gets murkier once swap is involved.
Should You Turn It On Today?
There's no single yes-or-no answer here, because the feature being stable doesn't mean it's the right fix for your specific memory problem. It means it's finally safe to test the question at all, on a workload where the question makes sense to ask.
| Your situation | Recommendation |
|---|---|
| All workloads run Guaranteed QoS | Skip it. Swap support doesn't affect you either way; Guaranteed pods never swap. |
| Burstable, cache-heavy, memory-spiky, fighting occasional OOM kills | Test it in staging now. This is the workload class the feature was built for. |
| Burstable batch or background jobs tolerant of some slowdown | Good candidate. Weigh a longer runtime against avoiding a restart-from-scratch OOM kill. |
| Burstable, latency-sensitive request handlers | Leave it off. Fix memory pressure with better resource requests or vertical scaling instead. |
| BestEffort workloads | Skip it. BestEffort pods don't get swap access regardless of node config. |
| Nodes still on cgroup v1 | Not available yet. Plan a cgroup v2 migration first if this is a priority. |
If you land in the "test it in staging" row, do exactly that before touching production: roll it out to one node pool, point a representative slice of Burstable traffic at it, and watch both the OOM-kill rate and p99 latency for at least a full traffic cycle (a week, ideally, to catch batch jobs and weekend traffic patterns) before deciding whether to expand it fleet-wide.
Watch node-level swap usage during that window, not just pod-level metrics. node_memory_SwapCached_bytes and node_memory_SwapFree_bytes from node_exporter, if you're already running Prometheus, tell you whether swap is being touched at all versus sitting idle because LimitedSwap capped usage tighter than expected. Pair that with your existing p99 latency dashboards for the same pods, and set an alert on sustained swap growth rather than any swap usage at all, since brief swap-ins during a traffic spike are exactly the behavior you're trying to enable.
Rolling back is simple if the staging window doesn't go well: set failSwapOn: false back with memorySwap.swapBehavior: NoSwap, restart the kubelet, and the node stops granting swap access on the next scheduling cycle. You don't need to disable swap on the node itself or drain it first, since NoSwap is safe to run indefinitely even with swap space provisioned and sitting unused.
Frequently Asked Questions
What is Kubernetes swap support?
It's a kubelet feature, stable as of Kubernetes 1.35, that allows Burstable QoS pods on a node to use swap space instead of being immediately evicted or OOMKilled when they exceed their memory request. It's opt-in per node through the kubelet's memorySwap configuration and requires swap to actually be provisioned on the node's disk first. Guaranteed and BestEffort pods never get swap access, regardless of node configuration.
What's the difference between LimitedSwap and NoSwap in kubelet config?
NoSwap lets the kubelet start on a node that has swap enabled, but pods still won't use that swap; it exists mainly so the kubelet doesn't refuse to run on a swap-enabled node. LimitedSwap is the setting that actually grants Burstable pods swap access, capped in proportion to the gap between their memory request and limit. If your goal is giving workloads real swap access, LimitedSwap is the one you want; NoSwap with swap provisioned on disk just wastes the disk space for no behavioral change.
Does enabling swap prevent OOMKilled pods?
It can reduce OOM kills for Burstable pods with spiky memory usage, since a pod can spill into swap instead of immediately hitting a hard limit. It doesn't eliminate OOM kills entirely: a pod that sustains memory usage well past what swap and its limit can cover will still get killed, and Guaranteed pods are never affected by swap at all since they never swap in the first place. Treat it as smoothing out brief spikes, not as a substitute for correctly sized memory limits.
Which pods get access to swap in Kubernetes?
Only pods in the Burstable QoS class, meaning at least one container sets a memory request lower than its limit. Guaranteed pods (request equals limit on every container) and BestEffort pods (no requests or limits set) never get swap access, no matter how the node's kubelet is configured. This is by design: Guaranteed QoS is specifically for workloads that need predictable memory without the latency variability swap introduces.
Can I enable swap on a managed Kubernetes service like EKS, GKE, or AKS?
Usually not, at least not without stepping outside the standard managed node pool experience. Swap support requires provisioning swap on the node's underlying disk and setting kubelet flags that managed node pools typically don't expose. Some managed offerings support this through a custom node image or a bootstrap script that runs before the kubelet starts, but check your provider's current documentation before assuming it's available, since this varies by provider and can change between releases.
How much does swap hurt performance if a pod actually starts using it?
It depends entirely on which pages get swapped. A memory access that hits swapped-out data goes from a nanosecond-scale RAM access to a millisecond-scale disk read, roughly a thousand-fold slowdown for that specific access. For a cache-heavy workload where only rarely-touched pages get swapped, that's barely noticeable in aggregate. For a latency-sensitive service where hot-path data ends up swapped, it shows up immediately as p99 latency spikes. This is exactly why the recommendation splits so sharply by workload type rather than being a blanket yes or no.
Related Articles
40 Kubernetes Interview Questions and Answers (2026)
40 Kubernetes interview questions covering Pods, Deployments, networking, RBAC, and real troubleshooting scenarios like CrashLoopBackOff and OOMKilled. Updated for 2026.
Go 1.26 Goroutine Leak Detection: A Practical Guide
Go 1.26 ships a real goroutine leak profiler. Here's how to enable it, read the profile output, and catch leaks in production before OOMKilled does it for you.