Skip to content
WindowsDeep Dive Published Updated 5 min readViews unavailable

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

Where WSL2 fits in Microsoft’s timeline

Microsoft announced the complete architectural rework that became WSL2 at Build in May 2019, a significant admission that WSL1’s syscall-translation approach — while clever — couldn’t reach full Linux kernel compatibility no matter how much translation-layer work went into it. WSL2 first reached general users via backport to Windows 10 versions 1903 and 1909, then became the default for new WSL installations with Windows 10 version 2004 in May 2020. GUI application support arrived later and separately: Microsoft previewed WSLg (Windows Subsystem for Linux GUI) at Build 2020 and shipped it in Windows 10 Insider builds the following year, finally reaching a production Windows release with Windows 11 — meaning Linux desktop applications with full graphics and audio support, run as ordinary windows on the Windows desktop rather than through a separate remote-desktop-style X server the user had to configure themselves, was a multi-year process layered on top of WSL2’s already-shipped kernel architecture rather than something that arrived alongside it.

The same lightweight-VM technology underlying Windows Sandbox

WSL2’s “lightweight utility VM” and Windows Sandbox (covered elsewhere on this blog) draw on the same underlying Hyper-V-derived virtualization platform rather than being two unrelated pieces of engineering — both need genuine hardware-enforced isolation and fast startup, and both get it from the same host-shared virtualization layer rather than a traditional, heavyweight full-VM stack. The specific difference is what each one virtualizes: Windows Sandbox shares the Windows OS image dynamically with the host and boots a disposable Windows desktop, while WSL2 boots an actual separate, Microsoft-maintained Linux kernel image with no equivalent host-image-sharing trick available, since the host isn’t running Linux at all — which is also why WSL2’s kernel has to be shipped and updated as its own independent component (via wsl --update) rather than inheriting anything from the host Windows kernel the way Windows Sandbox’s dynamic base image inherits from the host’s own OS files.

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.

Related:

Sources:

Comments