C Drive Full but WSL Says Otherwise? The "Grow-Only" Virtual Disk Trap — Reclaiming 150 GB in One Pass

Windows reported 652 GB used on the C drive, but df inside WSL showed only 135 GB. Where did the other 500+ GB go? The culprit turned out to be WSL2 and Docker Desktop vhdx files, which grow but never shrink: deleting files only marks space free internally, the file itself stays large. This post documents the hunt and the diskpart compact fix that reclaimed 150 GB.

The numbers didn’t add up

During a routine disk health check, I hit a discrepancy:

  • Windows showed the C drive at 949 GB with 652 GB used — in the red.
  • But df -h / inside WSL (Ubuntu) reported only 135 GB used.

That left 500+ GB unaccounted for. Common sense says deleting a file in Linux should shrink usage on the Windows side too — I’d deleted 62 GB of video output the night before, so the C drive should have loosened up considerably. It didn’t budge.

A mismatch like that is worth digging into.

The hunt: which pocket did the space go into

Rather than deleting things blindly, I mapped out where space was actually going. The C drive is too big (600+ GB) for a full du scan to be fast, so I asked Windows directly via PowerShell, sorted by folder size:

1
2
3
4
5
Get-ChildItem 'C:\' -Directory -Force | ForEach-Object {
  $s = (Get-ChildItem $_.FullName -Recurse -File -Force -ErrorAction SilentlyContinue |
        Measure-Object Length -Sum).Sum
  [PSCustomObject]@{ GB=[math]::Round($s/1GB,2); Name=$_.Name }
} | Sort-Object GB -Descending

The picture was immediately clear:

1
2
3
560.90 GB  Users          <- almost everything lives here
 35.45 GB  Program Files
 30.93 GB  Windows

Drilling into C:\Users\<me>\AppData\Local:

1
2
264.72 GB  wsl            <- WSL virtual disk
108.40 GB  Docker         <- Docker Desktop virtual disk

And the specific files:

1
2
C:\Users\li\AppData\Local\wsl\{...}\ext4.vhdx                  264.7 GB
C:\Users\li\AppData\Local\Docker\wsl\disk\docker_data.vhdx     108.3 GB

Two files totaling 373 GB. Culprit found.

Root cause: vhdx files only grow, never shrink

These two .vhdx files are the virtual hard disks for WSL2 and Docker Desktop. Your entire Linux system and all your Docker images ultimately live inside them.

The key is their growth strategy: grow-only.

Think of it like a closet you keep stuffing things into:

  • When you write files in Linux, Windows expands the vhdx on demand, making new “shelves.”
  • When you rm a file in Linux, it just marks that shelf as empty inside the closet — the closet itself (the vhdx file) doesn’t shrink at all.

That’s how you get this bizarre situation: the harder I deleted in Linux, the bigger the C drive got. Internally the vhdx already had 130 GB of “holes,” but Windows still saw the same 264 GB blob.

Same story for the Docker one — I’d just run docker system prune and cleared 20+ GB of images and build cache, yet docker_data.vhdx was still 108 GB.

The fix: manually compact to squeeze out the holes

Since it won’t shrink on its own, you compact it by hand. The idea is to mount the vhdx read-only, then use diskpart’s compact vdisk to physically squeeze out the internal holes.

Do this entirely on the Windows side, because you first need wsl --shutdown — you can’t compact a disk while it’s spinning.

Step 1: shut down WSL and Docker

In Windows, right-click the Start menu → Terminal (Admin) / PowerShell (Admin):

1
2
3
wsl --shutdown
# Make sure Docker Desktop isn't holding its vhdx
Get-Process "com.docker*" -ErrorAction SilentlyContinue | Stop-Process -Force

Step 2: compact the vhdx with diskpart

Run this for each vhdx (substitute your own path):

1
diskpart

At the DISKPART> prompt:

1
2
3
4
5
select vdisk file="C:\Users\li\AppData\Local\wsl\{your-GUID}\ext4.vhdx"
attach vdisk readonly
compact vdisk
detach vdisk
exit

compact vdisk runs for a few minutes (depending on file size and how many holes there are). Same for the Docker one:

1
2
3
4
5
select vdisk file="C:\Users\li\AppData\Local\Docker\wsl\disk\docker_data.vhdx"
attach vdisk readonly
compact vdisk
detach vdisk
exit

Can’t find your vhdx? Microsoft’s official one-liner (replace Ubuntu with your distro name):

1
2
(Get-ChildItem HKCU:\Software\Microsoft\Windows\CurrentVersion\Lxss |
  Where-Object { $_.GetValue("DistributionName") -eq 'Ubuntu' }).GetValue("BasePath") + "\ext4.vhdx"

Actual results

On my machine:

FileBeforeAfterReclaimed
WSL ext4.vhdx264.7G~140G~124G
Docker docker_data.vhdx108.3G~80G~28G

Free space on C jumped from 298 GB back to ~440 GB — 150 GB recovered. The process touches no Linux files; it just physically squeezes out the holes. Zero risk.

A few other bits of real junk I found along the way

  • The Docker trio: docker container prune, docker image prune -a, docker builder prune — cleared 20+ GB on this machine (dangling images + build cache). Note that image prune -a removes images not currently in use, so confirm nothing you need is stopped first.
  • VS Code extension leftovers: .vscode\extensions had 27 hidden .xxxxxxxx-xxxx GUID folders — stale copies left behind every time the Codex / Claude Code extensions updated, each carrying a 100–335 MB codex.exe/claude.exe. Clearing them freed 6.9 GB without touching the active extensions.
  • Package-manager caches: ~/.npm, ~/.bun/install/cache, ~/.cache, pip/pnpm stores — safe to delete, they rebuild automatically.

A few caveats

  1. Compacting a vhdx is safe, but save any unsaved work in WSL first — wsl --shutdown kills every terminal.
  2. If the compaction doesn’t reclaim much, run sudo fstrim -av inside WSL first (to mark free blocks), then run compact again from Windows — the recovery will be more thorough.
  3. For a permanent fix: newer WSL versions let you cap the vhdx size in .wslconfig and enable sparse vhdx (sparseVhd=true) so the file grows on demand and gives space back more aggressively. See Microsoft’s WSL disk-space docs.
  4. Don’t poke at WSL files under AppData with Windows Explorer/editors — you can corrupt the distro. To access Linux files from Windows, use the \\wsl$\<distro> share instead.

Closing thought

The math turned out to be simple: the C drive wasn’t full because I had too much stuff — it was full because two virtual disks only ate and never digested. Deleting files in Linux is a “logical delete”; on the Windows vhdx it becomes “marked but never reclaimed.”

So the next time your C drive fills up and you can’t tell why, don’t reach for a reinstall — map out where the space went first. Nine times out of ten it’s some grow-only container file quietly ballooning. Find it, compact it, and the space comes back.