DevOps
Kubernetes and Swap: Why It's Disabled by Default (And How That's Changing) - A kubeadm Debugging Story
Siddharth More Dev.to (EN Zone)
2 views
If you've ever bootstrapped a Kubernetes cluster from scratc h, you've probably hit this error at least once:
err="failed to run Kubelet: running with swap on is not supported, please disable swap or set --fail-swap-on flag to false"
I ran into it while setting up my own practice cluster on LXC containers using kubeadm. Everything else checked out - containerd (container runtime), cgroup drivers, network config - but kubelet flatly refused to start. The reason: swap was still enabled.
This post walks through why that happens, an LXC-specific nuance worth knowing, and what's actually changed in recent Kubernetes releases around swap support.
Quick Refresher: What Is Swap?
Swap is disk space (a partition or file) that the Linux kernel uses as overflow when physical RAM runs out, moving inactive memory pages to disk to free up RAM - slower than RAM, but it acts as a safety net against out-of-memory crashes.
Why Kubernetes Disables Swap By Default
By default, kubelet requires swap to be turned off on every node - if swap is on, kubelet won't even start, and you'll get exactly the error above.
The reasoning is about predictability. Kubernetes' scheduler and eviction system rely on accurate memory accounting to decide where to place pods and when to evict them under pressure. Swap breaks that: a pod could look like it's within its memory limit while actually thrashing against disk-backed swap, invisible to the scheduler. So historically, the safest default was simple: no swap, period.
The LXC-Specific Gotcha: Swap Isn't Namespaced
Here's a detail worth knowing if you're running Kubernetes nodes as LXC containers rather than full VMs: swap is a global, host-wide kernel resource - not namespaced like CPU, memory limits, or networking.
So /proc/swaps inside an LXC container reflects the host's swap state, not a per-container one. If the host has swap enabled, every LXC container on it - including your Kubernetes node - sees swap as "on" regardless of container-level settings. You can't hide it from the container alone.
Your real options in this setup:
Disable swap on the host (swapoff -a + comment out the entry in /etc/fstab) - the cleanest fix. (But I won't recommend)
Tell kubelet to tolerate swap being present via config (see next section) (Recommended).
Restrict swap at the cgroup level (memory.swap.max=0 on cgroup v2) as an extra safety net - though it limits usage, not detection, so kubelet still needs to be told to tolerate it.
How I Fixed It
Since I wanted to move fast without reprovisioning a separate swap-free host, I set failSwapOn: false in the kubelet configuration:
# /var/lib/kubelet/config.yaml
apiVersion: kubelet.config.k8s.io/v1beta1
kind: KubeletConfiguration
failSwapOn: false
Restart kubelet, and it starts up right away - running in NoSwap mode by default.
The Bigger Picture: KEP-2400 and Swap Support Going GA
While debugging this, I learned that Kubernetes' stance on swap has actually been evolving for years - and it's more nuanced than "swap = broken."
KEP-2400 (Node Memory Swap Support) started as alpha in Kubernetes 1.22 (2021) and reached GA in Kubernetes 1.34 (2025). It introduces the NodeSwap feature, controlled via kubelet configuration:
failSwapOn: false
memorySwap:
swapBehavior: LimitedSwap # or NoSwap
The two supported behaviors:
NoSwap - the default. Kubernetes workloads (pods) don't and can't use swap, but processes outside Kubernetes' scope - system daemons, and even kubelet itself - still can. This protects the node from system-level memory spikes without giving that safety net to workloads.
LimitedSwap - pods get a bounded, proportional amount of swap based on their memory requests, via cgroup v2. Guaranteed and BestEffort QoS pods get zero swap by design; only Burstable pods (requests < limits) get meaningful access.
An earlier mode, UnlimitedSwap, was removed in 1.30 - it let a single misbehaving container consume unlimited swap and potentially crash the node.
GA Doesn't Mean "Auto-Enabled"
Going GA doesn't mean kubelet now silently tolerates swap on upgrade. On the latest version, swap left on with no config changes still triggers the same startup error - the safe default hasn't moved. What's changed is there's now a stable, supported way to opt in, useful for things like JVM-based apps (Jenkins, SonarQube) that reserve large amounts of memory but rarely touch all of it, or as a buffer against short memory spikes instead of an outright OOMKill.
Swap on Kubernetes nodes is still an advanced, deliberate choice - control-plane nodes in particular are recommended to stay swap-free.
Takeaways
Kubelet requires swap off by default - unchanged even with KEP-2400 reaching GA in 1.34.
On LXC containers, remember swap is a host-level kernel resource, not namespaced.
failSwapOn: false gets kubelet running quickly (default NoSwap), but disabling swap at the host level is still the standard fix for a dedicated node.
If you want to use swap intentionally, LimitedSwap gives a bounded, QoS-aware way to do it - worth exploring for memory-heavy, low-active-usage workloads.
Read original: https://dev.to/siddharthajmore/kubernetes-and-swap-why-its-disabled-by-default-and-how-thats-changing-a-kubeadm-debugging-g26
← Previous
Why Code Diffs Are Not Enough for AI Agent Changes
Next →
The Thermal Shock Series #2: Designing a Thermal Shock Scoring Algorithm
Related
Introducing KDM-cli: Monitor Kubernetes & Docker from your Terminal
DevOps
0
DEV Community
I Used Docker Before I Understood It
DevOps
1
Dev.to (EN Zone)
SSH, Actually Explained: Handshakes, Keys, and the Tunnel Trick
DevOps
1
Dev.to (EN Zone)
[$] CERN's migration path from CentOS Linux to Debian
DevOps
1
LWN.net
Comments0
No comments yet — be the first