Working with WSL2
I've been using WSL2 for most of my Windows dev work for a while now, and I finally sat down to understand exactly what's happening under the hood, mostly because I kept hitting the same disk space issue every few months and got tired of googling the fix each time. This post covers what WSL2 is, where it's actually useful, the trade-offs, picking a distro, the commands I use most, connecting it to Docker, and that disk space issue in detail.
What WSL2 Actually Is
WSL1 worked by translating Linux system calls into Windows API calls on the fly. It was a clever approach but it had real limits: anything that needed actual kernel behavior, like Docker or proper namespaces, just didn't work. WSL2 does something different. It runs a real Linux kernel inside a lightweight Hyper-V virtual machine that Microsoft maintains.
That's the part that matters. WSL2 isn't emulating Linux, it's running it. Each distro you install gets its own instance of this VM, backed by its own virtual disk file called an ext4.vhdx, and they all share the same underlying kernel. This is also why Docker works properly on WSL2 but never did on WSL1. Containers need real cgroups and namespaces, and now they actually have them.
Where I'd Actually Use It
I use WSL2 mainly for running Node and Python exactly the way they'd run on a Linux server, and for Docker-based projects where I want the container behavior to match production. It's also been useful just for testing that a script behaves the same on Linux as it does on my Windows machine, without needing a second computer for it.
It starts to struggle the moment you need something close to bare metal. Heavy GPU work, high-throughput file I/O, custom kernel modules. WSL2 gets a lot closer to native Linux than a regular VM does, but it hasn't closed that gap completely.
Advantages I've noticed:
Fast boot: under 2 seconds, compared to 15-45 seconds for a regular VM
Real Docker support: because it's a real kernel underneath, not a translation layer
Good performance inside the Linux filesystem: builds and installs are close to native speed
Windows integration: shared clipboard, VS Code's Remote-WSL extension works well
Disadvantages:
Slow access to Windows files: anything under /mnt/c/ goes through a translation layer called 9P and it's noticeably slower
Kernel lag: the kernel is a Microsoft-maintained fork and it can be a version or two behind upstream
Disk space doesn't shrink on its own: more on this below, it's the whole reason I wrote this post
Picking a Distro
I went with Ubuntu because most tutorials and documentation assume it, but WSL supports a few others worth knowing about:
- Ubuntu, the default, best documentation
- Debian, a bit leaner
- Kali Linux, if you do security work
- openSUSE
- Alpine WSL, minimal footprint
Installing one is simple:
# See what's available using the following command
wsl --list --online
# Install a specific distro
wsl --install -d Ubuntu
# Confirm what's installed
wsl --list --verbose
💡 Pro Tip: you're not limited to one distro. I keep Ubuntu for daily work and spin up a throwaway Alpine instance when I just need a quick shell for testing something.
Commands I Actually Use
I don't use most of WSL's CLI surface day to day. These are the ones that come up constantly:
# Check status and version of installed distros
wsl --list --verbose
# Launch the default distro, or a specific one
wsl
orwsl -d Ubuntu
# Set a distro's WSL version, or make it default
wsl --set-version Ubuntu 2
wsl --set-default Ubuntu
# Shut everything down and release resources
wsl --shutdown
# Back up a distro before doing anything risky
wsl --export Ubuntu backup.tar
# Restore from backup or move a distro
wsl --import Ubuntu D:\WSL\Ubuntu backup.tar
# Delete a distro permanently
wsl --unregister Ubuntu
⚠️ Important: wsl --unregister deletes the entire distro including its virtual disk. If there's anything worth keeping, run wsl --export first.
Setting Up Docker
Docker Desktop uses WSL2 as its actual backend on Windows now, instead of running containers in a separate heavier VM. Setting it up:
Install WSL2 with at least one distro using wsl --install -d Ubuntu
Install Docker Desktop for Windows
In Docker Desktop, go to Settings → Resources → WSL Integration and turn on the distro you want Docker available in
From inside that distro, confirm it's working using following commands:
docker --version
docker run hello-world
Then check it's actually using the WSL2 backend:
docker info | grep -i "storage driver"
# Should show: Storage Driver: overlay2
📝 Note: you don't need Docker installed separately in each distro. Docker Desktop shares its engine across whichever distros you've enabled integration for.
Once this is working, docker build and docker run behave exactly like they would on a real Linux machine. Volumes mounted inside the Linux filesystem perform noticeably better than anything mounted through /mnt/c/.
The Disk Space Issue
This is the part I actually sat down to write this post about. You install packages, build some Docker images, process a dataset. Your WSL2 distro grows to 40GB. You clean everything up, delete the data, prune old images, check again. Still 40GB.
Nothing's broken here. WSL2 stores each distro inside a dynamically-expanding virtual disk file. That file grows automatically as you write data, but deleting files inside Linux only updates Linux's own internal bookkeeping. It doesn't tell Windows those blocks are free again. The vhdx file keeps holding onto every block it ever allocated until you explicitly tell it to let go.
Fixing it takes two steps, and missing either one is why people say "I tried this and nothing happened."
Step 1, inside WSL2 run:
sudo fstrim -av
This tells the Linux filesystem to mark the deleted blocks as actually free.
Step 2, in PowerShell, after shutting WSL down:
wsl --shutdown
# Find your distro's ext4.vhdx under:
# %LOCALAPPDATA%\Packages\<DistroPackageName>\LocalState\ext4.vhdx
# If you have the Hyper-V module, run:
Optimize-VHD -Path "C:\path\to\ext4.vhdx" -Mode Full
# Otherwise, diskpart works without it:
diskpart
select vdisk file="C:\path\to\ext4.vhdx"
attach vdisk readonly
compact vdisk
detach vdisk
exit
⚠️ Important: run fstrim before compacting, not instead of it. If you skip fstrim, the host has no way of knowing which blocks are actually free, and the compact step does nothing even though it reports success.
💡 Pro Tip: newer WSL versions have an experimental sparse mode that's supposed to shrink the disk automatically:
wsl --manage <DistroName> --set-sparse true
I tried this for a while and it worked inconsistently for me, so I went back to running fstrim and compact manually every month or so.
Wrapping Up
WSL2 has become the default way I run Linux on my Windows machine, and once I understood it's a real kernel in a lightweight VM rather than some kind of emulation, most of the weird behavior stopped feeling like bugs. The disk space thing is a good example of that. It's not broken, it's just a design decision that nobody bothers to explain clearly, and now I run fstrim and compact every month or two instead of being surprised by it.
If you're setting this up for the first time, start with Ubuntu, get Docker integration working early, and just build the disk cleanup into a habit instead of waiting until your SSD fills up to think about it.

Comments
Post a Comment