Measured on: Windows 11 Home 10.0.26200 · WSL 2.7.3.0 (kernel 6.6.114.1-1) · Ubuntu 26.04 LTS (fresh install) · 2026-08-06 · every size below read from the actual ext4.vhdx, both logical size and size-on-disk

The problem

My research distro once grew to ~47 GB — conda environments, Hugging Face caches, PyTorch checkpoints. I deleted files inside WSL, watched df -h drop, and the ext4.vhdx on the Windows side did not give back a single byte. Back then I gave up and nuked the whole distro with wsl --unregister.

While rebuilding it, I did what I should have done the first time: reproduced the bloat on purpose and measured every reclaim path people recommend. It turns out most of the advice you will find — fstrim first, enable sparseVhd=true — is outdated in 2026, and one of those options is now explicitly gated by Microsoft as unsafe.

Here is the reproduction, with real numbers at every step.

Reproducing the bloat

Fresh Ubuntu 26.04 distro. I wrote 20 GB of random data (random matters — zeroed blocks would let compact cheat) to simulate a model cache, then deleted it:

mkdir -p ~/fakecache && cd ~/fakecache
for i in 1 2 3 4; do dd if=/dev/urandom of=blob_$i bs=4M count=1280 status=none; done
# ... later ...
rm -rf ~/fakecache
Stepvhdx logical sizevhdx size on disk
Fresh distro1.41 GB1.41 GB
After writing 20 GB21.41 GB21.41 GB
After deleting all of it inside WSL21.41 GB21.41 GB

df -h inside WSL showed usage back down to 1.3 GB. The vhdx did not move. This is expected: the vhdx is a dynamically expanding virtual disk — it grows on write and never shrinks on its own.

So far this matches the old guides. Everything after this point does not.

Finding 1: you don’t need fstrim anymore

Every guide from 2020–2022 says the same thing: run sudo fstrim -a inside WSL first, then compact, otherwise the freed blocks won’t be reclaimable.

On current WSL that step is already done for you. Check the root mount:

$ grep -E ' / ' /proc/mounts
/dev/sdd / ext4 rw,relatime,discard,errors=remount-ro,data=ordered 0 0

discard is in the default mount options — deletes send TRIM to the virtual disk immediately, online. To verify, I went straight from rm to compact without any fstrim:

wsl --shutdown
diskpart /s compact.txt      # (elevated; script below)
Stepvhdx logical size
Before compact (garbage blocks, no fstrim)21.41 GB
After wsl --shutdown + diskpart compact1.38 GB

Full reclaim, zero fstrim. If a guide tells you compact won’t work without fstrim, it was written for an older WSL. (Running fstrim anyway is harmless — it just re-trims free space.)

The compact.txt script for diskpart (works on Windows Home, which has no Hyper-V Optimize-VHD):

select vdisk file="C:\Users\<you>\AppData\Local\wsl\{your-guid}\ext4.vhdx"
attach vdisk readonly
compact vdisk
detach vdisk
exit

Find your vhdx path with:

Get-ChildItem HKCU:\Software\Microsoft\Windows\CurrentVersion\Lxss |
  Get-ItemProperty | Select-Object DistributionName, BasePath

⚠ Double-check the file= path before running. diskpart operates on whatever you point it at.

Finding 2: sparse VHD is now disabled as unsafe

The other classic recommendation is sparseVhd=true in .wslconfig (or wsl --manage <distro> --set-sparse true) so the vhdx shrinks automatically. Here is what WSL 2.7.3 says today:

$ wsl --manage Ubuntu --set-sparse true
Sparse VHD support is currently disabled due to potential data corruption.
To force a distribution to use a sparse vhd, please run:
wsl.exe --manage <DistributionName> --set-sparse true --allow-unsafe
Error code: Wsl/Service/E_INVALIDARG

Microsoft gated the feature behind an --allow-unsafe flag because of data-corruption reports. That alone should end the “just enable sparse” advice for any distro whose contents you care about.

Since my distro was still empty scratch, I forced it on to measure what sparse mode actually does.

Finding 3: sparse mode works — but not where you’re looking

With sparse forced on, I repeated the 20 GB fill-and-delete. Five seconds after rm:

vhdx logical sizevhdx size on disk
Sparse on, 20 GB written21.38 GB21.38 GB
5 s after deleting inside WSL21.42 GB1.45 GB

Read that row again. The real disk usage auto-reclaimed within seconds — no shutdown, no compact. But the logical file size never shrinks, and that is the number Explorer’s default view, dir, and most cleanup tools show you.

This is, I believe, the entire source of the “sparse VHD doesn’t shrink” confusion in the Microsoft Q&A threads: sparse mode is working, but you are looking at the logical size. To see the truth, check the file’s Size on disk in Properties, or:

fsutil sparse queryflag <path-to-vhdx>   # is it sparse?
# size on disk ≈ "compressed" size:
(Get-Item <vhdx>).Length                 # logical

Finding 4: diskpart refuses sparse files — the exact error

And if you try to fix that big logical number with diskpart while the file is sparse:

DiskPart has encountered an error: The requested operation could not be completed
due to a virtual disk system limitation. Virtual hard disk files must be
uncompressed and unencrypted and must not be sparse.

(My Windows is Korean-locale; that is the canonical English text of the same error — it fails at attach vdisk.)

So with sparse enabled you get: real space reclaimed automatically, a scary-looking logical size you cannot compact away, and a corruption warning from Microsoft. That combination is why “wsl2 sparse vhd cannot compact” has so many unresolved threads.

Finding 5: turning sparse off re-inflates the file

One more trap on the way out. Converting back with --set-sparse false re-materializes the holes:

vhdx logical sizevhdx size on disk
Sparse on, after auto-reclaim21.39 GB1.45 GB
Right after --set-sparse false21.39 GB21.39 GB
After one final diskpart compact1.39 GB1.39 GB

If you ever used sparse mode and later disable it, budget the disk space for that re-inflation and finish with a compact.

What to actually do in 2026

Full measurement series (fresh Ubuntu 26.04, WSL 2.7.3.0):

#ActionLogicalOn disk
0Fresh distro1.41 GB1.41 GB
1Write 20 GB (random)21.4121.41
2Delete inside WSL21.4121.41
3wsl --shutdown + diskpart compact — no fstrim1.381.38
4--set-sparse true → blocked, needs --allow-unsafe
5(forced sparse) write 20 GB21.3821.38
6Delete → auto-reclaim in ~5 s21.421.45
7diskpart compact while sparse → fails
8--set-sparse false → re-inflates21.3921.39
9Final diskpart compact1.391.39

My recommendations, in order:

  1. Stay non-sparse (the default). When the vhdx gets fat, run wsl --shutdown then the diskpart script above. On WSL 2.7+ you can skip fstrim — discard is already in the mount options.
  2. Don’t force --allow-unsafe sparse on a distro you care about. Microsoft gated it for corruption risk; the auto-reclaim is nice but not worth your conda environments.
  3. If you already have sparse enabled and the file “won’t shrink”: check size on disk first — it probably already shrank. The logical size is cosmetic until you convert back (Finding 5).
  4. Prevent the bloat instead: the usual suspects are Hugging Face caches, pip/conda caches, and checkpoints. Moving HF_HOME out of the vhdx is the single biggest win — that’s the next post.

Everything above was measured on my own machine on 2026-08-06; timestamps and sizes are from the actual run. If you get different behavior on another WSL version, I want to know — see Contact.