A small PowerShell module for inspecting and maintaining WSL2 distros from the Windows host.
Warning
Optimize-WslVhdx operates on the live ext4.vhdx files that hold your WSL distro filesystems. Always back up important data inside your distros before running it — at minimum copy your home directory, project repos, and any databases out, or use wsl --export to snapshot a full distro to a tarball.
Caution
This software is provided "AS IS", without warranty of any kind, express or implied (see LICENSE). Tools that touch virtual disks can in rare cases corrupt them. You are solely responsible for any data loss or system damage. Read what each cmdlet does before running it; understand the steps; and keep backups.
| Cmdlet | What it does |
|---|---|
Get-WslDistro |
List installed distros with their state and vhdx paths. |
Get-WslDiskUsage |
Report each distro's vhdx size on the host plus a best-effort reclaimable estimate from inside the distro. |
Optimize-WslVhdx |
Reclaim space from a grown ext4.vhdx via diskpart. Self-elevates (UAC). |
Get-MsStoreAutoUpdate |
Report whether Microsoft Store background auto-updates are off (policy + scan tasks). Read-only, no admin. |
Disable-MsStoreAutoUpdate |
Stop Store from auto-updating WSL (and other Store apps) in the background. Self-elevates (UAC). |
Enable-MsStoreAutoUpdate |
Reverse of Disable-MsStoreAutoUpdate. Self-elevates (UAC). |
- Windows 10 / 11 with WSL2
- Windows PowerShell 5.1 or PowerShell 7+
- Administrator rights for
Optimize-WslVhdx(it will prompt via UAC) - Pester 5+ for tests (optional)
git clone https://github.com/cyharyanto/wsl-tools.git
Import-Module .\wsl-tools\WslToolsGet-WslDistro
Get-WslDiskUsage # default: best-effort inside-distro probe
Get-WslDiskUsage -HostOnly # skip inside probe (faster, no distro startup)
Optimize-WslVhdx -Distribution Ubuntu -VerboseSee examples/ for runnable scripts.
A common scenario: your Windows C: drive is filling up and you can't see where the space went. WSL2 stores each distro's filesystem in an ext4.vhdx file that grows as you write data but never shrinks on its own — even after you delete files inside the distro or run fstrim. After a year of development work, an Ubuntu vhdx can easily balloon to 200 GB on the host while only ~100 GB is actually used inside.
PS> Get-WslDiskUsage | Sort-Object VhdxSizeBytes -Descending
Name State Size UsedInside Reclaimable VhdxPath
---- ----- ---- ---------- ----------- --------
Ubuntu Stopped 205.25 GB 115.00 GB 90.25 GB C:\Users\you\AppData\Local\Packages\CanonicalGroupLim...\ext4.vhdx
Debian Stopped 26.38 GB 8.20 GB 18.18 GB C:\Users\you\AppData\Local\wsl\{...}\ext4.vhdx
archlinux Stopped 11.44 GB 6.10 GB 5.34 GB C:\Users\you\AppData\Local\wsl\{...}\ext4.vhdx
docker-desktop Stopped 112.00 MB C:\Users\you\AppData\Local\Docker\wsl\main\ext4.vhdxThe Ubuntu vhdx has roughly 90 GB of empty space inside it that is invisible to Windows. Reclaim it by compacting the vhdx.
Important
Quit Docker Desktop fully from the system tray before running Optimize-WslVhdx. While Docker Desktop is running, wsl --shutdown is effectively a no-op because Docker keeps the WSL2 utility VM alive, and the compact will fail with file is being used by another process.
PS> Optimize-WslVhdx -Distribution Ubuntu -RunFstrim -Verbose
Distribution : Ubuntu
VhdxPath : C:\Users\you\AppData\Local\Packages\CanonicalGroupLim...\ext4.vhdx
SizeBeforeBytes : 220336553984
SizeAfterBytes : 148771672064
SizeBeforeGB : 205.25
SizeAfterGB : 138.57
ReclaimedGB : 66.68What happened, end-to-end:
sudo fstrim -avran inside Ubuntu to mark unused blocks free.wsl --shutdownreleased the vhdx handle on the Windows side.- The cmdlet self-elevated via UAC and ran
diskpart compact vdiskagainst the file. - The Ubuntu vhdx shrank from 205.25 GB to 138.57 GB, freeing about 67 GB on the C: drive.
For "find the distro with the most reclaimable space and compact it" in one shot, see examples/Optimize-LargestDistro.ps1.
WSL itself ships as a Microsoft Store Appx package (MicrosoftCorporationII.WindowsSubsystemForLinux). Store updates the package in the background on its own schedule. If your Docker Desktop / running distros happen to be using C:\Program Files\WSL\* when Store fires, the MSI step of the upgrade can fail with:
MsiInstaller 11922 (Error) Error 1922. Service 'WSL Service' (WSLService) could not be deleted.
MsiInstaller 11708 Installation failed.
When that happens the WSL utility VM is restarted mid-failure, all your running distros (including Docker Desktop's docker-desktop distro) drop, and Docker bounces. Store then keeps retrying the same failing upgrade every few hours.
On Windows 11 build 26200 (Canary) and similar recent builds, the Microsoft Store UI no longer exposes a permanent "App updates off" toggle — only Pause 1–5 weeks. The two layers underneath that gate Store auto-updates still work though:
| Layer | Mechanism |
|---|---|
| Policy | HKLM\SOFTWARE\Policies\Microsoft\WindowsStore!AutoDownload = 2 — Store may notify, may not auto-install. |
| Background scan | \Microsoft\Windows\InstallService\ScanForUpdates and ScanForUpdatesAsUser scheduled tasks disabled — Store does not even check in the background. |
Apply both with one command:
Disable-MsStoreAutoUpdateReverse with Enable-MsStoreAutoUpdate. Check current state with Get-MsStoreAutoUpdate. Manual updates via Store → Library → Get updates still work — that path starts InstallService on demand and applies updates you explicitly choose. Run it right after a fresh Windows reboot when nothing holds WSL files open, and the install completes in seconds.
PS> Get-MsStoreAutoUpdate
Effective : Disabled
AutoDownloadPolicy : Disabled
AutoDownloadValue : 2
ScanForUpdates : Disabled
ScanForUpdatesAsUser : DisabledNote
On Canary/Insider builds Microsoft occasionally re-enables Store policies as part of telemetry. Re-run Get-MsStoreAutoUpdate after a cumulative update; rerun Disable-MsStoreAutoUpdate if anything came back.
If Docker Desktop is running, the WSL VM auto-restarts within seconds of wsl --shutdown — Docker Desktop holds the VM up and keeps vhdx file handles open. The diskpart compact then fails with file is being used by another process. Quit Docker Desktop fully (tray icon -> Quit) before calling Optimize-WslVhdx. The cmdlet pre-flights this and aborts with a clear message; pass -Force to override.
Docker Desktop registers a docker-desktop WSL distro whose registry path points at a small system vhdx (~100 MB). Docker stores its actual data in a separate docker_data.vhdx outside the standard WSL layout. This module reports only what the WSL registry exposes; manage Docker Desktop storage through Docker's own settings ("Resources -> Disk image size" / "Clean / Purge data").
By default, Windows blocks unsigned PowerShell modules under AllSigned policy. To use this module on your machine, allow local scripts for your user:
Set-ExecutionPolicy -Scope CurrentUser -ExecutionPolicy RemoteSignedOr run a one-off session with -ExecutionPolicy Bypass.
Invoke-Pester ./tests # unit tests, mocked, fast
Invoke-Pester ./tests -TagFilter Integration # real-WSL tests, opt-inMIT — see LICENSE.