DEV Community

Mahiro Hirakawa
Mahiro Hirakawa

Posted on

Deleting files inside WSL doesn't shrink the disk they lived on

Deleting files inside WSL doesn't shrink the disk they lived on

WSL2 keeps its entire Linux filesystem inside one file on the Windows side, a virtual hard disk with a .vhdx extension. Run cargo clean inside WSL, watch du report gigabytes freed, check the size of that .vhdx file on the Windows host afterward, and it hasn't moved. Not shrunk a little. Not moved at all. That gap confused me the first time and it's a completely ordinary consequence of how thin-provisioned virtual disks work, once you know to expect it.

The .vhdx grows on demand as the filesystem inside it needs more blocks, and Windows is happy to expand it automatically. It does not shrink on its own when those blocks get freed, because "freed" from the guest filesystem's point of view just means the blocks are marked available for reuse inside the container. Nothing tells the host container file "you can give some of your own size back now." That has to be requested explicitly, and it takes three steps run in a specific order, not one:

fstrim inside WSL first, which walks the filesystem and marks the actually-freed blocks as discardable, information the guest filesystem has but the host container doesn't yet. Then wsl --shutdown, which releases the running VM's hold on the file entirely, because you cannot resize a virtual disk file that a live process still has open. Only then does the compaction step, Optimize-VHD -Mode Full on the Windows side, or diskpart's compact vdisk as a fallback, actually rewrite the container file smaller. Skip any one of the three and the size doesn't change; do all three out of order and it doesn't change either.

The numbers from doing this on a real, heavily-used checkout: measured 2026-08-31, the container sat at 726.25GB while the filesystem inside it reported 648GB actually in use, meaning roughly 78GB was immediately reclaimable air even before any further cleanup. After a fuller pass a day later that also cleared out stale build artifacts inside the guest, the compaction step took the container from 845.4GB down to 173.4GB, a 672GB reduction confirmed by comparing the file size directly before and after, not by trusting a tool's summary line. That's a single measured run, not a claim about every WSL setup; your mileage depends entirely on how much stale, regenerable build output has accumulated inside the guest before you compact.

The part worth writing down separately from the mechanism: an earlier compaction on this same machine had already taken the container from 478GB down to 304GB, weeks before this round, and it re-swelled back up over about three weeks with no further attention. Compacting once doesn't compact forever. If nothing changes what's writing to the guest filesystem in the meantime, the container just fills back in.

Automating the three-step sequence surfaced its own small failure worth mentioning honestly: a scheduled-task version of the compaction script failed silently for a while, because it tried to request elevated permissions from inside a process that Task Scheduler had already launched as elevated, and the second request orphaned itself with no result log written at all. The fix was checking IsInRole(Administrator) directly instead of asking again. Separately, running the compaction while re-checking WSL's own status in the same session turned out to restart the WSL VM as a side effect of the status check itself, right before the shutdown step, which is a small, honest reminder that watching a system too closely while it's under test can change the thing you're trying to measure.

Top comments (0)