Docker Desktop on Windows uses WSL2 with dynamic VHDX files. They grow when adding images/containers but don’t shrink automatically when deleting them. Result: C: drive fills up, even though docker system df shows free space.

πŸ’‘ Solution: manual VHDX optimization via PowerShell + Docker utility.


πŸ“¦ Cleanup script

Create file

# File: $HOME\Scripts\docker-clear-wsl.ps1
$script = @'
$LOCAL = "$env:LOCALAPPDATA\Docker\wsl"
$VHD1 = Join-Path $LOCAL "disk\docker_data.vhdx"
$VHD2 = Join-Path $LOCAL "main\ext4.vhdx"

# 1. Docker cleanup
docker system prune -f

# 2. Reclaim space via official tool
docker run --rm --privileged --pid=host docker/desktop-reclaim-space
docker rmi docker/desktop-reclaim-space -f

# 3. Stop Docker Desktop
Get-Process -Name "Docker Desktop","com.docker.backend","com.docker.build" `
  -ErrorAction SilentlyContinue | Stop-Process -Force -ErrorAction SilentlyContinue

# 4. Shutdown WSL
wsl --shutdown

# 5. Optimize VHDX files (compact)
if (Test-Path $VHD1) { Optimize-VHD -Path $VHD1 -Mode Full }
if (Test-Path $VHD2) { Optimize-VHD -Path $VHD2 -Mode Full }

# 6. Restart Docker Desktop
Start-Sleep -Seconds 2
Start-Process -FilePath "$env:ProgramFiles\Docker\Docker\Docker Desktop.exe" `
  -ErrorAction SilentlyContinue
'@

$script | Out-File -FilePath "$HOME\Scripts\docker-clear-wsl.ps1" -Encoding UTF8

Run

# As Administrator (required for Optimize-VHD)
powershell.exe -ExecutionPolicy Bypass -File "$HOME\Scripts\docker-clear-wsl.ps1"

πŸ” How it works

StepCommandWhat it does
1docker system prune -fRemoves stopped containers, unused images, build cache
2docker/desktop-reclaim-spaceOfficial Docker tool to reclaim space in WSL2
3Stop-ProcessGracefully stops Docker Desktop (otherwise VHDX is locked)
4wsl --shutdownFully shuts down WSL, freeing files for optimization
5Optimize-VHD -Mode FullCompacts VHDX files, returning space to host
6Start-ProcessRestarts Docker Desktop

Why this way:

  • Without stopping processes, Optimize-VHD won’t work (file locked)
  • docker/desktop-reclaim-space works inside WSL, removing “ghost” data
  • Mode Full - maximum compression (slower, but more effective)

βš™οΈ Automation

Via Task Scheduler (GUI)

  1. Open Task Scheduler β†’ Create Basic Task
  2. Trigger: Weekly (e.g., Sunday, 03:00)
  3. Action: Start a program
    • Program: powershell.exe
    • Arguments: -ExecutionPolicy Bypass -File "C:\Users\ponfertato\Scripts\docker-clear-wsl.ps1"
  4. Settings: βœ… Run with highest privileges

Via PowerShell (scripted)

# Create task as Administrator
$action = New-ScheduledTaskAction -Execute "powershell.exe" `
  -Argument "-ExecutionPolicy Bypass -File `"$HOME\Scripts\docker-clear-wsl.ps1`""

$trigger = New-ScheduledTaskTrigger -Weekly -DaysOfWeek Sunday -At 3am

$principal = New-ScheduledTaskPrincipal -UserId "SYSTEM" -RunLevel Highest

Register-ScheduledTask -TaskName "Docker WSL Cleanup" `
  -Action $action -Trigger $trigger -Principal $principal

Verify and run manually

# Run task
Start-ScheduledTask -TaskName "Docker WSL Cleanup"

# Check status
Get-ScheduledTask -TaskName "Docker WSL Cleanup" | Get-ScheduledTaskInfo

# View history
Get-WinEvent -FilterHashtable @{LogName='Microsoft-Windows-TaskScheduler/Operational'; TaskName='Docker WSL Cleanup'} -MaxEvents 10

πŸ“Š Monitor results

Before and after

# VHDX size before cleanup
Get-ChildItem "$env:LOCALAPPDATA\Docker\wsl\disk\docker_data.vhdx" | Select Name, @{N="SizeGB";E={[math]::Round($_.Length/1GB,2)}}

# After cleanup - compare value

Docker stats

# Summary
docker system df

# Detailed
docker system df -v

Expected result:

  • docker system df shows less data
  • .vhdx file size on disk decreases (sometimes 2-5x)

⚠️ Safety and notes

Requirements

RequirementWhy it matters
Run as AdministratorOptimize-VHD requires elevated privileges
Close WSL-using appsOtherwise wsl --shutdown will fail
Backup critical dataIn case of failure (unlikely, but possible)

If something goes wrong

# Docker won't start after cleanup
β†’ Launch manually: "$env:ProgramFiles\Docker\Docker\Docker Desktop.exe"
β†’ Check logs: Get-Content "$env:LOCALAPPDATA\Docker\log.txt" -Tail 50

# VHDX didn't compact
β†’ Ensure Docker and WSL are fully stopped: wsl --list --verbose
β†’ Try manually: Optimize-VHD -Path "C:\path\to\docker_data.vhdx" -Mode Full

# "Access denied" error
β†’ Run PowerShell as Administrator
β†’ Check antivirus: may block VHDX access

Exclude from cleanup (optional)

To preserve specific images:

# Before prune, tag images
docker tag my-important-image my-important-image:keep
docker system prune -f --filter "label!=keep"

πŸ—‚ Pre-run checklist

  • Closed all apps using Docker/WSL
  • Launched PowerShell as Administrator
  • Checked free space on C: (need ~10% of VHDX size for compaction)
  • Ensured critical data is saved or tagged
  • Tested on one VHDX before full cleanup