Skip to content

Repository files navigation

wslcompact

License: MIT PowerShell 5.1+ Platform: Windows + WSL2

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
  >

Quick start

Run it once, without installing anything. Paste into PowerShell:

irm https://raw.githubusercontent.com/bigunmd/wslcompact/master/wslcompact.ps1 | iex

That opens the interactive picker. It elevates itself when it needs to.

Install it permanently

irm https://raw.githubusercontent.com/bigunmd/wslcompact/master/install.ps1 | iex

Then, from any shell:

wslcompact

The 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

Passing arguments to the one-liner

| iex cannot forward parameters. Use a script block instead:

& ([scriptblock]::Create((irm https://raw.githubusercontent.com/bigunmd/wslcompact/master/wslcompact.ps1))) -All -Clean -Force

Usage

wslcompact                                      # 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

Options

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.

What -Clean removes

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 packagesapt, dnf, yum, zypper, pacman, apk
  • Logsjournalctl --vacuum-size=50M

On a typical dev box the Docker build cache alone is the single largest item; 160 GB+ is not unusual.


Safety

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.


How it works

  1. Discovery — reads the Lxss registry hive rather than parsing wsl -l -v, so it works regardless of Windows display language. Any distro owning a .vhdx is 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.
  2. 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 of wsl -e bash -c "...".
  3. Trimfstrim -av as root inside the guest, so freed ext4 blocks are discarded at the block layer. Without this, compaction has little to reclaim.
  4. Shutdown — stops Docker Desktop if running, then wsl --shutdown to detach every virtual disk.
  5. Sparse (optional)wsl --manage <distro> --set-sparse true.
  6. CompactOptimize-VHD -Mode Full when Hyper-V is present, otherwise a diskpart compact vdisk script. Retries up to three times, since the disk can take a moment to release after shutdown.
  7. Report — before/after size per disk plus resulting free space per drive.

Requirements

  • Windows 10 2004+ or Windows 11, with WSL2
  • Windows PowerShell 5.1 or PowerShell 7+
  • Administrator rights, for compaction only. The script elevates itself; -ListOnly and -DryRun never do.
  • Hyper-V is not required — diskpart is used as a fallback.

Troubleshooting

"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.ps1

Compaction 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.


Contributing

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.

License

MIT