Install

Windows 10 22H2+ or Windows 11. In an elevated PowerShell:

wsl --install

One command. Microsoft's tool enables the Virtual Machine Platform and WSL features, downloads the latest WSL 2 kernel, installs Ubuntu by default, and reboots if needed. After reboot, an Ubuntu window opens for first-time user setup.

To pick a different distro:

wsl --list --online
# Available:
#   Ubuntu, Ubuntu-24.04, Debian, kali-linux, openSUSE-Tumbleweed, etc.

wsl --install -d Debian
wsl --install -d Ubuntu-24.04

Multiple distros, side by side

wsl --list -v
#   NAME             STATE           VERSION
# * Ubuntu           Running         2
#   Debian           Stopped         2
#   dev-throwaway    Stopped         2

# Default distro for unqualified `wsl` commands
wsl --setdefault Ubuntu

# Open a specific one
wsl -d Debian

You can have many independent distro instances; each has its own filesystem inside a per-distro VHDX file under %LOCALAPPDATA%\Packages\. Useful for keeping clean per-project environments.

The .wslconfig that matters

Create / edit %USERPROFILE%\.wslconfig (i.e. C:\Users\<you>\.wslconfig):

[wsl2]
# Memory cap for the WSL VM (default: 50% of Windows RAM, max 8 GB on small machines)
memory=16GB

# Number of vCPUs
processors=8

# Swap file size (set to 0 to disable; default = 25% of memory)
swap=4GB

# Reclaim memory back to Windows when WSL processes free it (was a bug in older builds)
autoMemoryReclaim=gradual

# Enable systemd inside WSL distros (default in newer builds; explicit here)
[boot]
systemd=true

# Use Mirrored networking mode: WSL shares the Windows IP stack
# Pro: localhost-bound services in Windows are reachable from WSL and vice-versa
# Con: some apps that bind to specific interfaces may not work
[experimental]
networkingMode=mirrored
dnsTunneling=true
firewall=true
autoProxy=true

Apply changes:

# In PowerShell (admin)
wsl --shutdown
# Then start any distro again; new config takes effect

systemd

WSL 2 supports systemd as of late 2022; on by default in recent distro releases. If systemctl status errors with "System has not been booted with systemd," edit /etc/wsl.conf inside the distro:

[boot]
systemd=true

Then wsl --shutdown from Windows + restart the distro. systemd boots; systemctl list-units shows it active. systemd timers (see that tutorial) work; system services like docker, postgres, redis all run normally.

Filesystem performance

The single biggest perf gotcha. Two filesystem realms:

  • Linux filesystem — under /, lives in the VHDX file. Native ext4 speed.
  • Windows filesystem — mounted at /mnt/c/, /mnt/d/, etc. 9P protocol bridge; orders of magnitude slower for many-small-files operations like git status on a large repo, npm install, cargo build.

Keep source code in /home/<user>/... inside the Linux filesystem. Edit from VS Code (it uses the WSL remote backend transparently) but never cd /mnt/c/Users/<you>/... for active development. The 10-100x speed difference is real.

VS Code integration

Install the "WSL" extension in Windows-side VS Code. Then from inside WSL:

cd ~/projects/myapp
code .

VS Code opens with the workspace running on the Windows side but the language servers, terminal, file watchers, and Git all running inside WSL. Effectively a Linux-native dev experience with Windows-side UI.

GPU passthrough

For ML workloads, WSL 2 supports CUDA / DirectML / Intel oneAPI. Install the Windows-side NVIDIA / AMD / Intel GPU driver as usual; WSL 2 auto-detects:

# Inside Ubuntu/Debian WSL
sudo apt install nvidia-cuda-toolkit
nvidia-smi
# Should show your GPU + CUDA version

Then PyTorch / TensorFlow / Ollama work with the GPU normally. The same Ollama tutorial (see that one) applies in WSL.

Networking quirks worth knowing

With default NAT networking, WSL distros are on a per-VM virtual network; Windows side reaches them via localhost:<port> for ports the distro binds. Other LAN hosts can't reach into WSL directly.

With networkingMode=mirrored (above), WSL shares the Windows IP stack — LAN hosts can reach WSL-bound services directly. Some VPN clients and apps that bind on specific interfaces break; revert to NAT mode if you hit issues.

For "I want to ssh from another laptop into a service running in WSL," with mirrored mode it just works on the Windows host's IP and the bound port (assuming Windows Firewall allows). With NAT mode, set up a port-proxy:

# In elevated PowerShell
netsh interface portproxy add v4tov4 listenport=2222 listenaddress=0.0.0.0 \
    connectport=22 connectaddress=$(wsl hostname -I).Trim()

# Allow through Windows Firewall
New-NetFirewallRule -DisplayName "WSL SSH" -Direction Inbound -LocalPort 2222 \
    -Protocol TCP -Action Allow

Docker Desktop vs Docker-in-WSL

  • Docker Desktop — commercial product (free for small companies). Runs Docker engine inside its own WSL distro, exposes CLI to all distros. Adds polish (UI, Kubernetes button).
  • Docker installed directly in your distroapt install docker.io inside Ubuntu/Debian WSL. Free, lower overhead, same daemon.

Either works. For polyglot multi-distro setups, Docker Desktop's shared engine is easier; for "I want one Ubuntu distro and it's my whole world," install Docker directly.

Common pitfalls

  • Editing Linux files from Windows (Explorer, Notepad++) corrupts permissions. Always edit via VS Code's WSL extension or directly inside the distro.
  • ~/.bashrc from a Linux machine often assumes /etc/hostname is the canonical name; WSL's hostname is Windows's hostname by default. Set hostname=my-wsl in /etc/wsl.conf if you want a stable one.
  • WSL ignores Windows-side proxy / VPN by default unless mirrored networking + autoProxy are on. Corporate-network developers should turn these on.