Understanding WSL2: How Windows Runs a Real Linux Kernel
How WSL2 differs fundamentally from WSL1's syscall translation, running an actual Linux kernel in a lightweight, tightly-integrated VM.
WSL2 is frequently described as “Linux on Windows,” but the more precise description is: a real, unmodified Linux kernel, running inside a purpose-built lightweight virtual machine, integrated into Windows closely enough that the seams mostly disappear. That’s a fundamentally different architecture from WSL1, and understanding the difference explains both WSL2’s strengths and its remaining rough edges.
WSL1: syscall translation, no real kernel
WSL1 worked by intercepting Linux syscalls at the kernel boundary and translating them into equivalent NT kernel operations — no Linux kernel ever actually ran. This made file I/O interop nearly seamless (a Linux process and a Windows process could touch the same file with native performance) but meant anything relying on kernel features the translation layer didn’t implement — full inotify semantics, certain networking primitives, actual Linux kernel modules — simply didn’t work.
WSL2: a real kernel in a lightweight VM
WSL2 instead runs a genuine, Microsoft-maintained Linux kernel inside a lightweight utility VM, built on the same underlying virtualization stack as Hyper-V but purpose-tuned for fast boot and low overhead rather than general-purpose VM flexibility.
wsl --status
wsl --list --verbose
wsl.exe -d Ubuntu
Because it’s a real kernel, everything that depends on genuine Linux kernel behavior — cgroups, real inotify, Docker running natively rather than through Docker Desktop’s own separate VM, strace, actual /proc semantics — just works, because it’s not being emulated or translated at all.
The kernel itself: shared, but replaceable
Every WSL2 distribution boots the same Microsoft-maintained kernel image by default, updated independently of Windows itself via Windows Update or manually:
wsl --update
# %UserProfile%\.wslconfig
[wsl2]
kernel=C:\\Users\\daniel\\custom-kernel
memory=8GB
processors=4
.wslconfig controls VM-wide settings (memory, CPU count, swap), while a per-distribution wsl.conf inside each distro controls things like automatic Windows drive mounting and systemd support.
# /etc/wsl.conf (inside the distro)
[boot]
systemd=true
Filesystem: two worlds, one performance cliff
WSL2 distributions have their own real ext4 virtual disk (a .vhdx file Windows manages), and Windows drives are reachable from inside WSL2 under /mnt/c/, /mnt/d/, etc. — but crossing that boundary carries a real performance cost, since every cross-filesystem access has to traverse the 9P protocol bridge between the Windows host and the Linux VM.
\\wsl$\Ubuntu\home\daniel\project
The practical rule of thumb: for anything performance-sensitive (a large git repo, a Node.js project with a big node_modules), keep the files inside the Linux filesystem (/home/...) rather than under /mnt/c/... — the difference is dramatic, not marginal, precisely because one path is native ext4 I/O and the other is crossing the virtualization boundary on every single file operation.
Networking: NAT by default, mirrored mode as an alternative
WSL2’s default networking mode puts the VM behind a NAT, with its own IP address distinct from the Windows host’s — which is why “localhost” behavior between Windows and WSL2 needed special-casing (localhost forwarding) to feel seamless. Newer versions support a mirrored networking mode, where the WSL2 VM shares the host’s network interfaces directly, closer to how a container’s network namespace can share the host’s network stack:
# .wslconfig
[wsl2]
networkingMode=mirrored
GPU and systemd support
Modern WSL2 supports GPU passthrough (via a virtual GPU driver bridging into the host’s actual GPU) for CUDA/ML workloads, and — since a specific Windows/WSL update — real systemd as PID 1 inside the distro rather than a minimal init shim, which is what unlocked running unmodified systemctl-managed services and full snap/docker.service-style tooling directly inside WSL2 without workarounds.
wsl --list --online
Why this architecture is the right trade-off
WSL1’s syscall-translation approach optimized for tight Windows integration at the cost of Linux kernel fidelity; WSL2 inverts that trade-off, running a real kernel for full compatibility at the cost of a (very thin, but real) virtualization boundary for cross-OS file and network access. For development workloads — which is WSL’s primary use case — that trade strongly favors WSL2: correctness and compatibility with the actual Linux tools developers rely on matters more than the last percentage points of cross-filesystem I/O performance, especially once the “keep your working files inside the Linux filesystem” habit is established.