Skip to content
WSL2 Cheat/sheet

Configuration reference

What each setting does, its default, and when to change it. Recipes link here; you rarely need to read it top to bottom.

Config files

.wslconfig settings#

Every setting in %USERPROFILE%\.wslconfig, the file that sizes and networks the whole WSL2 virtual machine, with defaults and when to change each one.

.wslconfig is one file on the Windows side that configures the single virtual machine all your distros share. Per-distro settings (systemd, default user, PATH) live in /etc/wsl.conf instead.

Changes apply only after a full restart of the VM: run wsl --shutdown in PowerShell, wait about 8 seconds, then open a terminal again. Closing terminal tabs is not enough. If nothing changes, see .wslconfig changes do nothing.

Example file#

A sensible starting point for a 32 GB development machine. Every line is optional; leave out what you don’t need.

File%USERPROFILE%.wslconfig
# Applies after: wsl --shutdown   (wait ~8 s, then reopen a terminal)
[wsl2]
memory=16GB                # ~50–75% of your RAM
processors=8               # all threads, or all minus 2
swap=8GB                   # ~25–50% of memory
networkingMode=mirrored    # WSL shares Windows' IPs; localhost works both ways
dnsTunneling=true          # fixes DNS on VPNs and odd networks
firewall=true              # Windows firewall rules apply to WSL
autoProxy=true             # use the Windows proxy settings
guiApplications=true       # WSLg: run Linux GUI apps
nestedVirtualization=true  # KVM / Android emulators inside WSL

[experimental]
autoMemoryReclaim=dropcache  # give idle RAM back to Windows
sparseVhd=true               # virtual disk shrinks when you delete files
hostAddressLoopback=true     # Windows can reach services bound to WSL's LAN IP

Sizing memory and swap#

Your RAMmemoryswap
16 GB8–10GB4GB
32 GB16–24GB8GB
64 GB32–48GB8–16GB

Leave Windows enough for the browser, editor UI and any Android emulator, which all run on the Windows side. How to apply and check a cap: Limit WSL memory and CPU.

[wsl2] section#

memory#

Default: 50% of Windows RAM. The most memory the VM may use. Linux fills spare RAM with file cache, so without a cap vmmemWSL can grow until Windows feels slow. Lower it if Windows apps starve; raise it for large builds, many containers or big test suites. Recipe: Limit WSL memory and CPU. Symptom: WSL uses too much memory.

processors#

Default: all logical processors. The number of CPU threads Linux sees (nproc). Lower it only if Windows needs guaranteed headroom while builds run, for example during calls or screen recording.

swap#

Default: 25% of memory, rounded up to a whole GB. Swap space on a separate virtual disk. Set it to 25–50% of memory. swap=0 disables it, which turns memory pressure into processes being killed instead of slowing down.

networkingMode#

Default: nat. mirrored makes WSL share Windows’ network interfaces and IP addresses: localhost works in both directions and devices on your LAN can reach WSL ports. Needs Windows 11 22H2 or later. Comparison and which mode suits which connection: Networking modes. Recipe: Turn on mirrored networking.

dnsTunneling#

Default: on in current WSL releases (older releases: off). DNS requests from Linux are answered by Windows instead of over the virtual network, so DNS keeps working with VPNs and unusual network setups. Leave it on unless you have a specific reason. Symptom: DNS fails after connecting to a VPN.

firewall#

Default: true on Windows 11 22H2+. Applies Windows Firewall and Hyper-V firewall rules to WSL traffic. In mirrored mode this is what blocks a phone from reaching your dev server until you open the port: Open a WSL port to your local network.

autoProxy#

Default: true. Passes the Windows HTTP proxy settings into Linux as environment variables. Useful on corporate networks; harmless elsewhere.

guiApplications#

Default: true. WSLg: Linux GUI apps such as gitk or meld open as normal Windows windows. Set false to save a little memory if you never use them.

nestedVirtualization#

Default: true on Windows 11. Lets Linux use KVM, for example to run an Android emulator or a VM inside WSL. Most setups run the Android emulator on Windows instead: Run React Native with the Android emulator.

localhostForwarding#

Default: true. In NAT mode, a server listening in WSL is reachable from Windows at localhost. Ignored in mirrored mode, where localhost is shared anyway.

[experimental] section#

Experimental keys can change or graduate between WSL releases. Check them against the official WSL config docs or the WSL Settings app.

autoMemoryReclaim#

Values: disabled, gradual, dropcache. The default differs between WSL versions. Gives memory Linux is only using as cache back to Windows. gradual releases it slowly; dropcache drops the cache once WSL has been idle. Either keeps vmmemWSL from holding RAM it no longer needs.

sparseVhd#

Default: false. New distros get a sparse virtual disk that shrinks when you delete files, instead of only ever growing. Recipe: Shrink the WSL virtual disk.

hostAddressLoopback#

Default: false. Mirrored mode only. Lets Windows (and containers) reach a service in WSL through the machine’s LAN IP, not just through localhost. Turn it on if a tool insists on connecting to your LAN address.

Config files

/etc/wsl.conf settings#

Per-distro settings in /etc/wsl.conf (systemd, default user, Windows PATH, drive mounts, DNS file) with defaults and the consequence of changing each.

/etc/wsl.conf lives inside each distro and only affects that distro. Settings for the whole VM (memory, networking) are in .wslconfig.

Edit it with sudo (for example sudo nano /etc/wsl.conf). Changes apply after wsl --shutdown in PowerShell. Make a copy before editing, so a typo is easy to undo:

WSLBack up before editing
sudo cp /etc/wsl.conf /etc/wsl.conf.bak

Example file#

File/etc/wsl.conf (sudo)
[boot]
systemd=true

[user]
default=yourname           # also used when you restore a backup

[interop]
enabled=true
appendWindowsPath=true     # false = faster shell, but code/explorer.exe disappear

[automount]
enabled=true
options="metadata,umask=22,fmask=11"   # real Linux permissions on /mnt/c

[network]
generateResolvConf=true    # let WSL manage /etc/resolv.conf

Settings#

boot systemd#

[boot] systemd=true. Default: on for Ubuntu installed with wsl --install. Runs systemd as PID 1, which Docker Engine, timers (fstrim.timer) and systemctl need. Check with ps -p 1 -o comm=: it should print systemd. If systemctl is-system-running says degraded, see systemd reports degraded.

user default#

[user] default=yourname. Default: the user you created on first launch, stored by Windows rather than in this file. Write it here anyway: a distro restored with wsl --import reads it from this file, and otherwise logs you in as root. See Back up and restore your WSL distro.

interop enabled#

[interop] enabled=true. Default: true. Lets Linux start Windows programs (explorer.exe ., clip.exe, code). Turning it off breaks the VS Code code command and every Windows tool call.

interop appendWindowsPath#

[interop] appendWindowsPath=true. Default: true. Adds the whole Windows PATH to Linux. Convenient, because code and explorer.exe just work, but it slows command lookup and can let the Windows node or npm shadow the Linux ones (Node or npm is the Windows one).

Setting it to false is optional tuning with a consequence: code, explorer.exe and powershell.exe stop being found until you add them back by hand. How to do that properly: Call Windows apps without the Windows PATH.

automount options#

[automount] options="metadata,umask=22,fmask=11". Default: no metadata. Without metadata, files under /mnt/c all look executable and permission changes don’t stick. With it, chmod works there and Git stops reporting mode changes. Code should still live on the Linux filesystem: Where files live.

network generateResolvConf#

[network] generateResolvConf=true. Default: true. WSL rewrites /etc/resolv.conf on every start, which is why hand edits to that file disappear. Only set false if you deliberately manage DNS yourself, as a last resort described in DNS fails after connecting to a VPN. Prefer dnsTunneling in .wslconfig.

network hostname#

[network] hostname=devbox. Default: the Windows computer name. Changes the Linux hostname, which shows up in prompts and in key titles like $(hostname) WSL.

Concepts

Where files live#

Why code belongs on the Linux filesystem (~/dev) and not on /mnt/c, with a detailed comparison, a layout that scales, and how to reach the files from Windows.

Linux files sit on an ext4 virtual disk (ext4.vhdx). /mnt/c is the Windows drive, reached from Linux over a network-style file-sharing bridge. Every file operation that crosses that bridge is slow, and file-change events often don’t cross it at all.

The rule: keep code in your Linux home (~/dev), and run tools from Linux on it. The baseline setup creates that folder.

Comparison#

~/dev (Linux disk)/mnt/c (Windows disk)
pnpm install, builds, git statusNative speed5–20× slower
File watchers (Vite, Next, Metro)Instant reloadOften miss changes
Permissions, symlinks, exec bitsCorrectEmulated, Git noise
Docker bind mountsFastSlow
Line endingsLFCRLF slips in
Defender scanningNot scanned per fileEvery file scanned

The reverse also holds: Windows tools working on Linux files through \\wsl.localhost are slow. That’s why the editor runs its language servers inside Linux (Open a WSL project in your editor).

A layout that scales#

WSL~/dev
~/dev/
├── projects/    # repos, optionally projects/<org>/<repo>
├── infra/       # shared docker-compose (Postgres, Redis…)
└── sandbox/     # throwaway experiments

infra/ is where shared Postgres and Redis go.

Reaching the files from Windows#

  • In Explorer: \\wsl.localhost\Ubuntu\home\<you>\dev. Pin it to Quick Access.
  • From the Linux shell: explorer.exe . opens the current folder.
  • Convert paths: wslpath -w . gives the Windows path, wslpath 'C:\Users' the Linux one.

Don’t mix tools across the boundary#

Use Windows Git and Node on Windows files, and WSL Git and Node on WSL files. Running Windows Git on a Linux repo (or the reverse) rewrites line endings and file modes and confuses lockfiles. If it already happened: Every file shows as changed.

/mnt/c is fine for downloads, design assets and one-off copies:

WSLCopy something in from Windows
cp /mnt/c/Users/<you>/Downloads/x.zip ~/dev/sandbox/
Why /mnt/c is slowWhy

WSL2 is a real Linux kernel in a lightweight VM. Its own disk is a block device it controls directly. Windows drives are shared into the VM over the 9P protocol: every stat, open and read becomes a request to a server on the Windows side. Package managers and Git perform tens of thousands of those per command. Windows also doesn’t deliver its file-change notifications as Linux inotify events, so watchers on /mnt/c fall back to polling or miss changes.

Concepts

Networking modes#

How WSL2 networking works in NAT and mirrored mode, what each can reach, and which recipe to use for each connection scenario.

WSL2 runs in a lightweight VM, so it has its own network stack. networkingMode in .wslconfig decides how that stack is joined to Windows.

NAT and mirrored compared#

NAT (default)Mirrored
WSL’s IP addressPrivate 172.x address, changes on restartSame IPs as Windows
Windows → WSL on localhostYes (localhost forwarding)Yes
WSL → Windows on localhostNo; use the Windows host IPYes
Phone / LAN → WSLOnly via netsh portproxy rulesYes, after a firewall rule
VPN compatibilityOften breaks DNS or routingBetter, with dnsTunneling
RequiresAny WSL2Windows 11 22H2+

Mirrored is the better default for development on Windows 11. Recipe: Turn on mirrored networking.

Which recipe for which connection#

You want to reach…From…Recipe
A dev server in WSLA Windows browserWorks in both modes. Not working? A port is already in use
A service on WindowsWSLTurn on mirrored networking
A dev server in WSLA phone or another computerOpen a WSL port to your local network, then Open your Expo app on a phone or Test a local HTTPS site on your phone
A service in WSLA Docker containerConnect a container to a service in WSL
The internetWSL, while on a VPNDNS fails after connecting to a VPN
The Windows adb serverWSLRun React Native with the Android emulator

Listening addresses#

Whether something is reachable also depends on the address the server listens on. Check with:

WSLWhat's listening, and on which interface?
ss -tlnp                     # 0.0.0.0 / [::] = reachable from LAN; 127.0.0.1 = local only
Listens onReachable from
127.0.0.1 / ::1The same machine (Windows and WSL share it in mirrored mode)
0.0.0.0 / [::]Every interface: containers, LAN, phones (subject to firewall)

Most dev servers listen on localhost by default and need a flag such as --host to accept outside connections.

How NAT mode forwards localhostWhy

In NAT mode, WSL gets an address on a private virtual switch. A helper (wslrelay) watches for ports opening in Linux and listens on the same port on Windows’ localhost, forwarding traffic in. That’s why localhost:3000 works from a Windows browser, and why Get-NetTCPConnection on Windows shows wslrelay owning ports your Linux server opened. Nothing forwards in the other direction or from the LAN.

Concepts

Kernel and resource limits#

The Linux kernel, file-handle and Docker limits that matter for large repositories, what they default to, and which recipe changes each.

Ubuntu’s defaults suit small servers, not monorepos with 200,000 files and several watchers. You only need to change these when you hit the matching symptom; none are part of the baseline setup.

Kernel settings (sysctl)#

Persisted in a file under /etc/sysctl.d/, applied with sudo sysctl --system. Check a current value with sysctl <name>.

SettingTypical defaultSuggestedWhat it limits
fs.inotify.max_user_watches8192–65536524288Files all watchers together can watch
fs.inotify.max_user_instances1281024Separate watcher processes
fs.file-maxLarge on modern kernels2097152Open files system-wide
vm.max_map_count65530262144Memory maps per process (Elasticsearch, some JVM tools)
vm.swappiness6010How eagerly Linux swaps instead of dropping cache

Watchers#

ENOSPC: System limit for number of file watchers reached, or hot reload stops on a large repo. Recipe: Raise file-watcher limits. Symptom: Changes do not trigger hot reload.

vm.max_map_count#

Only needed for Elasticsearch/OpenSearch (which refuse to start below 262144) and some JVM tools. Set it in the same sysctl file as the watcher limits.

vm.swappiness#

Lower values keep build processes in RAM rather than swap. Only worth changing if you see heavy swap use with a memory cap in .wslconfig.

Open-file limits (nofile)#

The per-process limit on open files, checked with ulimit -n. Ubuntu’s soft default is 1024. Jest, Metro and bundlers on large repos can exceed it and fail with EMFILE: too many open files. It has two places: PAM limits for login shells and DefaultLimitNOFILE for systemd services. Recipe: Raise open-file limits. Symptom: Too many open files.

Docker limits#

Set in /etc/docker/daemon.json (Docker Engine in WSL).

SettingDefaultSuggestedWhy
log-driver / log-optsjson-file, unlimitedlocal, max-size: 20m, max-file: 3Container logs otherwise grow without bound
builder.gc.defaultKeepStorageUnlimited30GBCaps the BuildKit build cache

Recipe: Install Docker in WSL. Disk symptom: Windows disk space is running out.

Disk TRIM#

fstrim.timer (systemd) tells the virtual disk which blocks are free, so a sparse VHD can actually shrink. Recipe: Shrink the WSL virtual disk.

VM-wide limits#

Memory, CPU and swap for the whole VM are set in Windows, not in Linux: .wslconfig and Limit WSL memory and CPU.