Skip to main content

Updated Sep 3, 2026

A Full-System VM, and What WSL2 Can Do

A Debian VM for the labs that need systemd and real block devices, then an honest account of which labs WSL2 can and cannot run.

The BusyBox initramfs lab that carried this folder so far is deliberately thin: no systemd, no real block device, no network stack, because none of that is needed to watch scheduling, memory management, or a syscall happen. That thinness runs out the moment a later lab needs any of those three things for real, and rebuilding them by hand inside an initramfs would just be reinventing a distribution one directory at a time. A Debian cloud image under QEMU is the second machine this section keeps around for exactly that reason — slower to boot, but a real installed system with real services. And because a sizeable share of readers are doing all of this from Windows, the second half of this page turns the same honesty this section applies everywhere else onto WSL2 itself: what it actually runs, what it fakes, and where it will teach you something wrong if you don't already know better.

When the initramfs lab is not enough​

NeedFolderWhy the initramfs lab can't cover it
A running init system, units, and service dependencies03BusyBox's /init is a shell script, not systemd — there is nothing to introspect with systemctl.
A real block device and an on-disk filesystem11–12The initramfs lab's root is tmpfs, unpacked entirely into RAM; nothing here ever calls into a block layer or a filesystem driver.
A working network stack13The canonical invocation configures no NIC; anything below the socket layer is simply absent.
Containers15Namespaces and cgroups work in principle inside the initramfs lab, but a container runtime expects a real root filesystem, systemd (or an init that fakes enough of it), and a network stack to attach to.

Each row names the folder where that lab actually lives; this page just builds the machine that makes it possible.

A Debian cloud image under QEMU​

Debian publishes ready-to-boot cloud images per release, and the genericcloud variant — a smaller hardware-driver set than generic, built for exactly this kind of VM use — is the one to grab. The path below is current for Debian 13 ("trixie") as of this writing; check cloud.debian.org/images/cloud before you run it, since the release name in the path changes every time Debian ships a new stable version:

curl -LO https://cloud.debian.org/images/cloud/trixie/latest/debian-13-genericcloud-amd64.qcow2

A cloud image has no root password and no user account baked in on purpose — it expects to be provisioned on first boot by cloud-init, reading configuration from a small ISO it looks for at every boot. cloud-localds (Debian/Ubuntu package cloud-image-utils) builds that ISO from two YAML files:

cat > user-data <<'EOF'
#cloud-config
users:
- name: debian
ssh_authorized_keys:
- ssh-ed25519 AAAA...your-public-key...
sudo: ALL=(ALL) NOPASSWD:ALL
shell: /bin/bash
EOF
touch meta-data

sudo apt install --yes cloud-image-utils
cloud-localds seed.iso user-data meta-data

Boot the image with the seed attached as a second drive, the cloud image as a virtio block device, and a forwarded port standing in for a real network:

qemu-system-x86_64 \
-drive file=debian-13-genericcloud-amd64.qcow2,if=virtio \
-drive file=seed.iso,if=virtio,format=raw \
-netdev user,id=n0,hostfwd=tcp::2222-:22 \
-device virtio-net-pci,netdev=n0 \
-m 2G \
-smp 2 \
-enable-kvm \
-nographic

Give cloud-init a few seconds to run on first boot, then reach the guest over the forwarded port instead of the serial console:

ssh -p 2222 debian@localhost

Everything from here — apt, systemctl, a real /etc/fstab — behaves exactly like an installed Debian machine, because it is one.

Snapshots are the point​

A machine with real state is a machine you can break in a way that sticks, which is exactly what some later labs need to demonstrate — and exactly what makes doing it carelessly expensive. qemu-img snapshots make the qcow2 disk itself revertible, independent of anything happening inside the guest:

qemu-img snapshot -c before debian-13-genericcloud-amd64.qcow2

Then break the machine deliberately — corrupt a filesystem, rm -rf something a service depends on, whatever the lab in question calls for — and when it's time to start clean again:

qemu-img snapshot -l debian-13-genericcloud-amd64.qcow2 # list snapshots
qemu-img snapshot -a before debian-13-genericcloud-amd64.qcow2 # roll back to "before"

This is what makes the destructive labs later in this section safe to run at all: a :::danger lab is one whose damage is real and does not undo itself, and a snapshot taken first is what turns "real and permanent" into "real and free to repeat." The Lab Machine covers the badge; this is the mechanism that backs it for anything running on this full-system VM.

Sharing files with the guest​

Two ways to move a file across the host/guest boundary, with a real trade-off between them. scp over the forwarded port (scp -P 2222 file debian@localhost:) needs nothing beyond what the invocation above already sets up, but it's a copy — the guest gets its own point-in-time version, and a host-side edit after the fact does not appear on the other end without copying again. virtiofs shares a host directory live instead: run virtiofsd --socket-path=/tmp/vhostqemu -o source=/path/on/host on the host, add -chardev socket,id=char0,path=/tmp/vhostqemu -device vhost-user-fs-pci,chardev=char0,tag=hostshare -object memory-backend-memfd,id=mem,size=2G,share=on -numa node,memdev=mem to the QEMU invocation (the shared memory object has to match -m), then mount -t virtiofs hostshare /mnt inside the guest — after which both sides see the same files, no copying, no staleness. scp is the simpler tool for "get this one file across once"; virtiofs is worth the extra setup only when the guest needs to keep working against files the host is actively editing.

WSL2, honestly​

WSL2 is not a compatibility shim and it is not bare metal either — it's a Microsoft-built Linux kernel, compiled from a public fork Microsoft maintains, booted inside a lightweight Hyper-V VM with a curated set of virtio drivers and Hyper-V-specific integration. That kernel is real: it runs real syscalls, a real scheduler, a real VFS. What's missing is everything upstream of the kernel — there is no firmware, no boot loader, no discovered hardware, because Hyper-V hands the VM a kernel image directly the same way -kernel does in the canonical QEMU invocation from Booting Your Kernel in QEMU. That single fact — a real kernel, with the entire boot chain in front of it replaced — is what makes the table below need three columns instead of two.

CapabilityWorksPartlyDoes not
Building a kernelYes
Most userspace tooling (gcc, git, editors, containers via WSL's own runtime)Yes
straceDepends on the shipped kernel's CONFIG_ set for the tracee's syscalls
perfHardware PMU counters are Hyper-V's to grant, not guaranteed on every host
/proc and /sysPopulated by the real kernel, but for a synthetic Hyper-V machine, not your physical one
Running a custom kernelYes — via kernel= in .wslconfig
UEFI/GRUB boot chainHyper-V hands the VM a kernel image directly; nothing here ever boots
kexec / kdumpAbsent — no boot chain to hand off to
systemdYes on current WSL, opt-in via wsl.conf's [boot] section on older ones
Loading arbitrary out-of-tree modulesOnly against a matching custom kernel you built yourself (kernelModules= in .wslconfig)
Nested QEMU with KVM accelerationYes on recent Windows 11 builds, via nestedVirtualization in .wslconfig

The "partly" rows share a cause: WSL2's kernel is a real kernel, so /proc/cpuinfo, /sys, strace, and perf all return something — the trap is that "something" describes the Hyper-V VM Microsoft built, filtered through whatever that kernel's CONFIG_ set enables, not the physical machine sitting under your hands.

Which folders' labs run on WSL2​

FolderRuns on WSL2?Why
00 — OverviewYesNo lab content; reading only.
01 — Lab and ToolchainPartlyBuilding a kernel and any-linux labs run fine; QEMU labs need nestedVirtualization on and a recent Windows 11 build, and GDB against -s -S works the same way once that's true.
02 — Guided TracesPartlyTraces that run inside this section's own QEMU lab inherit folder 01's nested-virtualization requirement; traces read directly off WSL2's own kernel are not the same kernel this section pins and built against.
03 — systemd and InitPartlyCurrent WSL2 ships systemd (opt-in on older versions), so systemctl-level exploration works; anything that needs to watch the actual boot handoff from firmware does not, because WSL2 has no boot chain to watch.
04 — Kernel Architecture and IdiomsPartlyReading source and building are any-linux work and run anywhere; anything that depends on this section's QEMU/GDB lab inherits the same nested-virtualization requirement as folder 01.

This is the table the wsl2-ok badge means: a lab earns it only once nested virtualization has been confirmed working, and most qemu/qemu-gdb labs in folders 05 and later have not been re-verified against it, which is why this table stops at folder 04.

The honest summary​

WSL2 is a Microsoft-built Linux kernel running inside a Hyper-V VM, with a heavily customised init and filesystem path in front of it. It is a genuinely good place to read kernel source and to build a kernel — both are ordinary userspace work that doesn't care what's underneath it. It is a bad place to learn how a machine boots, because the entire boot chain this section spends folder 03 on simply isn't there to observe. And it will teach you something wrong about /proc and /sys if you trust their contents uncritically, because what they describe is real, but it's a description of the synthetic Hyper-V machine, not of the laptop or desktop those files seem to be talking about.

References​