devAlice
← Windows

WSL2 tuning — memory, systemd, DNS, I/O in one pass

The seven things to fix right after installing WSL2 — .wslconfig, systemd, DNS, disk reclaim, VS Code integration, mirrored networking.

If you've installed WSL2 via Windows initial setup, the next step is tuning. Running on defaults leads to runaway memory, broken DNS after reboot, ballooning disk, and missing systemd. This guide covers seven tuning steps in one pass — apply once, benefit daily.

Target: Windows 11 (22H2+) + WSL2 + Ubuntu (or any systemd-based distro).

TL;DR

TuningFileEffect
1. .wslconfig%USERPROFILE%\.wslconfig (Windows)RAM/CPU/swap caps + GUI · mirrored networking
2. wsl.conf systemd/etc/wsl.conf (inside WSL)Enables systemd → snap · docker · systemctl all work
3. DNS stability/etc/wsl.conf + /etc/resolv.confPrevents DNS drops after VPN / reboot
4. I/O performanceWorkspace locationUse WSL-native (/home/...) instead of /mnt/c/...
5. Disk reclaimexport/importShrinks bloated vhdx (tens of GB recoverable)
6. VS Code Remote-WSLOne extensionEdit WSL files at native speed
7. Mirrored networking.wslconfig networkingMode=mirroredVPN/corp-friendly (Win11 22H2+)

Prerequisites

  • Windows 11 22H2 or newer (some features prefer 23H2+)
  • WSL2 installed + at least one systemd-capable distro (Ubuntu, etc.)
    • Not installed: in admin PowerShell, wsl --install
  • Admin rights recommended for wsl --shutdown (.wslconfig itself is in the user profile)

1. .wslconfig — RAM/CPU/swap caps

By default WSL2 can grab up to ~50% of Windows RAM — a large Node build can push Windows itself into swap. Set explicit caps.

1.1 Template

.wslconfig
# PowerShell
Invoke-WebRequest -Uri https://devalice.jaceclub.com/assets/windows/wsl-tuning/wslconfig-template.txt -OutFile $env:USERPROFILE\.wslconfig
Get-FileHash $env:USERPROFILE\.wslconfig -Algorithm SHA256
# expected: BFBDAF7AE486916D8AA4E792F9845416E1B6B6EA535F84E1BBCCA2BE9ACCB3C4
# (lowercase: bfbdaf7ae486916d8aa4e792f9845416e1b6b6ea535f84e1bbcca2be9accb3c4)

1.2 Key settings

# %USERPROFILE%\.wslconfig
[wsl2]
memory=8GB             # 50–75% of host RAM
swap=4GB               # disk swap (0 = disabled)
localhostForwarding=true
guiApplications=true   # Linux GUI apps (Win11 default)
# processors=8         # explicit core cap (optional)

1.3 Apply

wsl --shutdown   # stop all WSL instances
wsl              # next start picks up .wslconfig

1.4 Verify

Inside WSL:

free -h          # memory cap matches (e.g. 8GB)
nproc            # CPU count
swapon --show    # swap active

A 16GB host machine works well with memory=10GB. A 32GB machine: memory=16GB.

2. Enable systemd — /etc/wsl.conf

WSL2's default init is not systemd. Modern packages (docker, snap, postgresql service, etc.) expect it.

2.1 Enable

# inside WSL (sudo required)
sudo nano /etc/wsl.conf

Contents:

[boot]
systemd=true
 
[user]
default=me          # default user (optional)

Save, then:

# Windows PowerShell
wsl --shutdown
wsl

2.2 Verify

ps -p 1            # PID 1 == systemd → success
systemctl status   # systemd info shown → OK

Now sudo systemctl start docker and friends work properly.

Boot time grows by 1–3 seconds; in exchange you get a real service manager, journal logging, and tab completion.

3. DNS stability

WSL2's default DNS goes through a Windows NAT bridge that often breaks after VPN connect/disconnect or reboot. Symptom: curl github.com works but apt update doesn't, or vice versa.

3.1 Option A — disable auto-DNS + static resolvers

sudo nano /etc/wsl.conf

Add:

[network]
generateResolvConf=false

Save → wsl --shutdown → restart.

Now WSL stops rewriting /etc/resolv.conf. Set it manually:

sudo nano /etc/resolv.conf
nameserver 1.1.1.1
nameserver 8.8.8.8

On corp networks/VPN, add the internal DNS your IT provides.

3.2 Option B — mirrored networking (Win11 22H2+, recommended)

Section 7 makes WSL share Windows' DNS automatically. Most stable on corporate VPN.

4. I/O performance — where to put your work

The most commonly missed tuning. Performance differs sharply:

LocationPathBuild speed (e.g. npm install)
WSL native/home/me/project✅ Fast (Linux ext4)
Windows mount/mnt/c/Users/me/project❌ 5–10× slower (9p protocol)

Rule: WSL work lives inside the WSL disk. If Windows needs the same files, use VS Code Remote-WSL (§6) or Syncthing (see file sync) to keep copies on both sides.

4.1 Good pattern

# inside WSL
mkdir -p ~/work
cd ~/work
git clone git@github.com:org/repo.git
cd repo
npm install              # fast

4.2 Anti-pattern

# clone on Windows, build via WSL
cd /mnt/c/Users/me/Documents/project   # cross-mount
npm install                             # slow (tens of minutes)

4.3 When cross-mount is needed

  • A Windows-only tool must open the same folder (Photoshop, etc. — rare)
  • Editing from Windows IDE — solvable with VS Code Remote-WSL

5. Disk size — vhdx compaction

WSL's .vhdx virtual disk never shrinks automatically. Build a 30GB cache, delete it — the file stays at 30GB.

5.1 Check current size

# PowerShell
Get-ChildItem -Path "$env:LOCALAPPDATA\Packages" -Recurse -Filter "ext4.vhdx" | ForEach-Object {
  "{0,-60} {1:N2} GB" -f $_.Directory.Name, ($_.Length / 1GB)
}

5.2 Option A — Sparse VHD (Win11 22H2+)

In .wslconfig (already commented out in the §1.1 template):

[experimental]
sparseVhd=true

wsl --shutdown enables it. Unused space is reclaimed incrementally.

5.3 Option B — manual compaction (diskpart)

While WSL is shut down:

wsl --shutdown
 
diskpart
# diskpart>
select vdisk file="C:\Users\me\AppData\Local\Packages\CanonicalGroupLimited.Ubuntu_*\LocalState\ext4.vhdx"
attach vdisk readonly
compact vdisk
detach vdisk
exit

Tens of GB recoverable. Slow — run overnight.

5.4 Option C — Export/Import (cleanest)

Full backup + recreate. The new vhdx starts at optimal size.

wsl --shutdown
wsl --export Ubuntu C:\backup\ubuntu.tar
wsl --unregister Ubuntu
wsl --import Ubuntu C:\WSL\Ubuntu C:\backup\ubuntu.tar

Option C is the most effective but the instance ID / install metadata are recreated. Re-apply systemd config, default user, etc. Best done during a planned migration.

6. VS Code Remote-WSL

Edit WSL files from Windows VS Code at native speed. The standard answer to the cross-mount problem.

6.1 Install

In Windows VS Code marketplace:

  • Remote - WSL (ms-vscode-remote.remote-wsl)

Or from the command line:

code --install-extension ms-vscode-remote.remote-wsl

6.2 Use

Inside WSL:

cd ~/work/repo
code .

→ Windows VS Code opens, bottom-left shows WSL: Ubuntu. Filesystem, terminal, and extensions all run inside WSL.

Alternatively, in Windows VS Code: Command Palette → WSL: Connect to WSL → pick a folder.

6.3 Extensions to install on the WSL side

On first connect, some extensions ask "Install on WSL?". Recommended yeses:

  • ESLint / Prettier
  • Python / Pylance
  • Go / Rust-Analyzer
  • Docker

7. Mirrored networking (Win11 22H2+)

A more elegant fix than §3. WSL mirrors Windows' network interface directly — VPN, corp network, DNS, and localhost behave identically to the host.

7.1 Enable

In .wslconfig:

[wsl2]
networkingMode=mirrored
 
[experimental]
dnsTunneling=true
autoProxy=true

wsl --shutdown and restart.

7.2 Effects

  • VPN — WSL automatically goes through the VPN
  • Corporate proxy — Windows' IE/Edge proxy is picked up automatically
  • localhost — Windows ↔ WSL is bidirectional (no localhostForwarding needed)
  • DNS — same as Windows, most stable

7.3 Caveats

  • Conflicts with some Docker Desktop builds — disable temporarily if you hit issues
  • Some advanced container networking scenarios behave differently — verify if you do deep container work

Verification

The five checks:

  1. Inside WSL, free -h shows the cap from .wslconfig (e.g. 8.0Gi)
  2. ps -p 1 shows systemd
  3. curl -sS https://www.google.com -o /dev/null && echo OK → OK (DNS/network)
  4. Both cd ~ && touch test.txt && ls and cd /mnt/c/Users/$USER && touch test.txt && ls work (only the speed differs)
  5. From VS Code, code . shows WSL: Ubuntu in the bottom-left

Troubleshooting

.wslconfig changes don't apply

  • Forgot wsl --shutdown. All WSL instances must fully stop before the next start picks up the file.
  • Location must be %USERPROFILE%\.wslconfig (your user home), not %LOCALAPPDATA%.
  • Filename must be .wslconfig (leading dot, no extension).

Boot fails after enabling systemd

  • Common cause: a syntax mistake in /etc/wsl.conf (e.g. missing [boot] header).
  • Recovery: from PowerShell, wsl -u root -d Ubuntu to enter as root and fix /etc/wsl.conf.

apt update returns DNS errors

  • Apply §3.1 (manual /etc/resolv.conf), or switch to §7 mirrored networking.
  • On VPN, ensure the corporate DNS is added.

npm install is unbearably slow

  • §4 — you're working from /mnt/c/.... Move to ~/work.

VS Code Remote-WSL fails to connect

  • code not on PATH. Open VS Code on Windows once → Command Palette → Shell Command: Install 'code' command in PATH.
  • Verify with code -v inside WSL.

Docker Desktop breaks after mirrored networking

  • Docker Desktop was assuming NAT mode. Reconfigure its WSL integration, or disable mirrored mode while using Docker.

vhdx doesn't shrink (§5)

  • diskpart's compact vdisk is limited by fragmentation. Option C (export/import) is cleanest.

References

Changelog

  • 2026-05-12 — Initial English translation (devAlice M2 i18n seed)

Comments