WSL2 Gotchas: The Things That Bite You After a Year of Daily Use
WSL2 is a fantastic development environment on Windows. It's also a system with sharp edges that the official docs rarely highlight — the kind you only discover after losing an afternoon to a process eating 300% CPU for no apparent reason.
This guide documents four specific problems I've hit repeatedly over the last year while using WSL2 as my main development environment for Docker-based projects. For each one: the root cause, why the obvious fix doesn't work, and what actually solves it.
This isn't an introduction to WSL2. If you're already using it daily and something feels off, keep reading.
- Docker Desktop, cgroups, and processes that ignore resource limits
The symptom
You run a container on Docker Desktop for Windows (which uses WSL2 under the hood). The container executes a CPU-intensive process — a vulnerability scanner, a compiler, a batch job.
You watch htop and the process is consuming 300%+ CPU, dragging the entire system down.
You think: "no problem, I'll throttle it."
services:
heavy-worker:
image: my-scanner:latest
deploy:
resources:
limits:
cpus: '1.0'
memory: 1G
cpu_count: 1
You restart the container.
Still 300% CPU.
You try nice, ionice, cpulimit... nothing works.
The root cause
Docker Desktop runs containers inside a WSL2-hosted VM using cgroup v2, often with limited controllers.
The result:
deploy.resources.limits → ignored
cpu_count → ignored
nice / ionice → ineffective
You can verify:
cat /sys/fs/cgroup/cgroup.controllers
You'll often see fewer controllers than on a native Linux system.
What actually works
Option A (best): Limit the WSL2 VM
[wsl2]
processors=4
memory=8GB
swap=2GB
Then:
wsl --shutdown
Now the VM is capped → containers behave predictably.
Option B: Use tool-level throttling
Examples:
--parallel=1
--low-mem
These bypass scheduler issues entirely.
Option C: Replace the tool
If it’s designed to max all cores and can't be tuned → wrong tool for WSL2.
Key takeaway
Don’t trust container limits on WSL2. Control the VM or use self-throttling tools.
- Disk performance: /mnt/c vs native WSL filesystem
The symptom
Working in:
/mnt/c/Users/you/projects
npm install → 8 minutes
git status → 4 seconds
Move to:
~/projects
npm install → 25 seconds
git status → instant
The root cause
/mnt/c uses the 9P protocol, meaning every filesystem call crosses the Windows ↔ Linux boundary.
Heavy IO workloads (Node, Git, Docker builds) suffer massive slowdowns.
The native WSL filesystem is ext4 inside a VHDX, which runs at near-native Linux speed.
Real benchmark
Operation /mnt/c WSL native
npm install ~8 min ~25 sec
git status (10k files) ~4 sec < 100 ms
docker build context ~90 sec ~3 sec
What actually works
Rule: Keep code inside WSL
~/projects/myapp
Access options:
VS Code + WSL extension (best)
\wsl$\Ubuntu\home\you\projects
If you MUST use /mnt/c
Move heavy directories (node_modules, .git) to WSL
Use symlinks
Key takeaway
/mnt/c is for compatibility, not performance.
- Networking: ports, shifting IPs, and host access
The symptom
localhost:3000 → sometimes works
WSL IP changes every reboot
LAN access doesn’t work
The root cause
WSL2 networking is:
NATed via Hyper-V
Not bridged
Dynamic IP
Partial localhost forwarding
What actually works
Ensure localhost forwarding
[wsl2]
localhostForwarding=true
Get WSL IP
ip addr show eth0 | grep 'inet ' | awk '{print $2}' | cut -d/ -f1
Expose to LAN (port proxy)
$wslIP = (wsl hostname -I).Trim().Split(' ')[0]
netsh interface portproxy add v4tov4
listenport=3000 listenaddress=0.0.0.0
connectport=3000 connectaddress=$wslIP
New-NetFirewallRule -DisplayName "WSL 3000"
-Direction Inbound -LocalPort 3000
-Protocol TCP -Action Allow
Best solution (modern WSL)
[wsl2]
networkingMode=mirrored
✔ Same network as host
✔ No NAT issues
✔ LAN works directly
Key takeaway
WSL2 networking = NAT. Use mirrored mode if available.
- Memory: the vmmem problem
The symptom
You stop containers
WSL is idle
vmmem still using huge RAM
The root cause
WSL2:
Allocates memory dynamically
Does NOT release it automatically
Linux keeps cache
Windows cannot reclaim it
What actually works
Cap memory
[wsl2]
memory=8GB
swap=4GB
Enable auto reclaim
[experimental]
autoMemoryReclaim=gradual
sparseVhd=true
Manual reclaim
sudo sh -c "echo 3 > /proc/sys/vm/drop_caches"
Last resort
wsl --shutdown
Key takeaway
WSL will NOT give memory back unless you configure it.
Minimal .wslconfig (recommended)
[wsl2]
memory=8GB
processors=4
swap=2GB
localhostForwarding=true
networkingMode=mirrored
[experimental]
autoMemoryReclaim=gradual
sparseVhd=true
📍 Path:
C:\Users<you>.wslconfig
Apply:
wsl --shutdown
Closing thoughts
Most of these issues come from one fact:
WSL2 is a VM pretending to be native Linux.
And the cracks show when you push it with real workloads.
Once you understand:
cgroups quirks
/mnt/c performance trap
NAT networking
memory ballooning
Everything becomes predictable.
If you've hit other WSL2 gotchas, drop them in the comments 👇
The one that surprised me most?
Spending 3 days tuning container limits… that were being completely ignored.
💡 Did this save you an afternoon? A follow or reaction helps me write more of these.
SOCIAL SHARE CARD GENERATOR