Reclaim Windows disk space from WSL2 virtual disks.
A WSL2 ext4.vhdx grows on demand but never shrinks on its own. You can delete
200 GB inside your distro, watch df -h report it as free, and Windows will still
show the same enormous file. wslcompact closes that gap: it cleans the guest,
discards the freed blocks, and compacts the virtual disk so the space comes back
to your Windows drive.
==> Detecting WSL2 virtual disks
# Distro Size Drive State Disk
--- ---------------------------- ------------ ----- ------- ----
1 Ubuntu-24.04 365.73 GB D: Running D:\WSL\Ubuntu-24.04\ext4.vhdx
2 docker-desktop-data 45.10 GB C: Stopped C:\Users\me\AppData\Local\Docker\wsl\disk\docker_data.vhdx
Select disks to process.
<Enter> or 'a' = all '2' = one '1,3' = several '1-3' = range 'q' = quit
>
Run it once, without installing anything. Paste into PowerShell:
irm https://raw.githubusercontent.com/bigunmd/wslcompact/master/wslcompact.ps1 | iexThat opens the interactive picker. It elevates itself when it needs to.
irm https://raw.githubusercontent.com/bigunmd/wslcompact/master/install.ps1 | iexThen, from any shell:
wslcompactThe installer drops the script and a .cmd shim into
%LOCALAPPDATA%\Programs\wslcompact and adds it to your user PATH.
No admin rights needed to install. To remove it:
& ([scriptblock]::Create((irm https://raw.githubusercontent.com/bigunmd/wslcompact/master/install.ps1))) -Uninstall| iex cannot forward parameters. Use a script block instead:
& ([scriptblock]::Create((irm https://raw.githubusercontent.com/bigunmd/wslcompact/master/wslcompact.ps1))) -All -Clean -Forcewslcompact # interactive picker, trim + compact
wslcompact -ListOnly # show what was found, change nothing
wslcompact -DryRun # show every action, perform none
wslcompact -All -Clean -Force # unattended full reclaim
wslcompact -Distro Ubuntu-24.04 -Clean -SetSparse
wslcompact -All -Clean -PruneBuildArtifacts # also drop Cargo target/ dirs| Flag | Effect |
|---|---|
-Distro <names> |
Operate on specific distros. Skips the picker. |
-All |
Operate on every detected disk. Skips the picker. |
-ListOnly |
Print detected disks and exit. |
-DryRun |
Print every action without doing it. Never elevates. |
-Clean |
Clean inside the guest before trimming (see below). |
-PruneBuildArtifacts |
Delete Cargo target/ dirs under -ProjectRoot. |
-ProjectRoot <path> |
Guest path to scan. Default: the distro user's home. |
-PruneVolumes |
Destructive. Also prune unused Docker volumes. |
-SetSparse |
Mark disks sparse so Windows reclaims blocks automatically. |
-SkipCompact |
Clean and trim only, leave the .vhdx alone. |
-Force |
Skip confirmations. Implies -All when no -Distro is given. |
Everything is guarded by a command -v check, so it adapts to whatever the
distro actually has installed.
- Docker — build cache, stopped containers, unreferenced images
- Language tooling — Go build cache,
uv,pip,pnpm,yarn,npm, Homebrew,ccache, Poetry - System packages —
apt,dnf,yum,zypper,pacman,apk - Logs —
journalctl --vacuum-size=50M
On a typical dev box the Docker build cache alone is the single largest item; 160 GB+ is not unusual.
Docker volumes are never touched unless you pass -PruneVolumes. That flag
runs docker volume prune -af, which permanently deletes the contents of every
volume not attached to a running container. If you have stopped project
databases, their data is in those volumes. Leave the flag off unless you are
certain.
-PruneBuildArtifacts only removes directories named target that contain
Cargo's CACHEDIR.TAG marker file, so an unrelated folder called target is
never touched.
Everything else the tool deletes is a cache that regenerates itself, at the cost of a slower next build.
Use -DryRun first if you want to see the plan before committing to it.
- Discovery — reads the
Lxssregistry hive rather than parsingwsl -l -v, so it works regardless of Windows display language. Any distro owning a.vhdxis WSL2; WSL1 distros have no virtual disk and are ignored. Docker Desktop's data disk lives outside the hive on recent versions, so a few known locations are probed too. - Clean (optional) — stages a shell script into the guest as a temp file
with LF endings and runs it via
bash, which sidesteps the CRLF and quoting problems ofwsl -e bash -c "...". - Trim —
fstrim -avas root inside the guest, so freed ext4 blocks are discarded at the block layer. Without this, compaction has little to reclaim. - Shutdown — stops Docker Desktop if running, then
wsl --shutdownto detach every virtual disk. - Sparse (optional) —
wsl --manage <distro> --set-sparse true. - Compact —
Optimize-VHD -Mode Fullwhen Hyper-V is present, otherwise adiskpartcompact vdiskscript. Retries up to three times, since the disk can take a moment to release after shutdown. - Report — before/after size per disk plus resulting free space per drive.
- Windows 10 2004+ or Windows 11, with WSL2
- Windows PowerShell 5.1 or PowerShell 7+
- Administrator rights, for compaction only. The script elevates itself;
-ListOnlyand-DryRunnever do. - Hyper-V is not required —
diskpartis used as a fallback.
"Running scripts is disabled on this system" The one-liners bypass this already. If you saved the file locally, run it with:
powershell -ExecutionPolicy Bypass -File .\wslcompact.ps1Compaction fails or frees nothing
The disk was probably still attached. The script retries three times, but if
Docker Desktop or a background VS Code Server keeps restarting the distro, close
them and try again. Also confirm fstrim actually reported trimmed bytes — with
nothing trimmed there is nothing to compact.
Space freed inside WSL but the .vhdx is unchanged
That is the normal state before compaction, and exactly what this tool fixes.
Run it once with -SetSparse to have Windows reclaim blocks automatically going
forward.
No disks found
Your distros may be WSL1. Check with wsl -l -v; convert with
wsl --set-version <distro> 2.
Bug reports and pull requests are welcome. Because this tool deletes things on other people's machines, the review bar is mostly about safety — see CONTRIBUTING.md for the ground rules and the four checks to run before opening a PR.
Found a security issue? Please report it privately rather than opening an issue: SECURITY.md.
This project follows the Contributor Covenant.
MIT