Skip to content

Instantly share code, notes, and snippets.

@erm3nda
Created August 26, 2026 11:44
Show Gist options
  • Select an option

  • Save erm3nda/1fd6b8b5b7c672d56564c65b8234fbef to your computer and use it in GitHub Desktop.

Select an option

Save erm3nda/1fd6b8b5b7c672d56564c65b8234fbef to your computer and use it in GitHub Desktop.
Reduce vhd docker disk size
# reduce-docker-vhd.ps1
# Script to locate and shrink/compact the Docker Desktop WSL2 virtual disk (docker_data.vhdx)
$ErrorActionPreference = "Stop"
# 1. Locate the VHDX file
$vhdxPath = "$env:LOCALAPPDATA\Docker\wsl\disk\docker_data.vhdx"
if (-not (Test-Path $vhdxPath)) {
# Search for any docker_data.vhdx or ext4.vhdx in the AppData\Local\Docker folder as a fallback
$fallbackSearch = Get-ChildItem -Path "$env:LOCALAPPDATA\Docker" -Include "docker_data.vhdx", "ext4.vhdx" -Recurse -ErrorAction SilentlyContinue | Sort-Object Length -Descending | Select-Object -First 1
if ($fallbackSearch) {
$vhdxPath = $fallbackSearch.FullName
Write-Host "[+] Found VHDX at fallback location: $vhdxPath" -ForegroundColor Yellow
} else {
Write-Host "[-] Could not locate any docker_data.vhdx or ext4.vhdx file. Please ensure Docker Desktop is installed." -ForegroundColor Red
Exit
}
}
# 2. Get initial size
$initialFile = Get-Item $vhdxPath
$initialSizeGB = [math]::Round($initialFile.Length / 1GB, 2)
Write-Host "[*] Located Docker VHDX: $vhdxPath" -ForegroundColor Cyan
Write-Host "[*] Current size on disk: $initialSizeGB GB ($($initialFile.Length) bytes)" -ForegroundColor Cyan
# 3. Prompt user to close Docker Desktop
Write-Host "`n[!] IMPORTANT: Docker Desktop must be fully closed/exited before compacting." -ForegroundColor Yellow
Write-Host "[*] Please close Docker Desktop from your system tray now." -ForegroundColor Yellow
Read-Host "Press ENTER once Docker Desktop is closed to proceed with shutting down WSL"
# 4. Shut down WSL
Write-Host "`n[*] Stopping WSL instances..." -ForegroundColor Cyan
wsl --shutdown
Start-Sleep -Seconds 3
# Double-check if the file is still locked
try {
$fileStream = [System.IO.File]::Open($vhdxPath, 'Open', 'ReadWrite', 'None')
$fileStream.Close()
Write-Host "[+] VHDX file is unlocked and ready for compaction." -ForegroundColor Green
} catch {
Write-Host "[-] The VHDX file is still locked by another process (WSL or Docker might still be closing)." -ForegroundColor Red
Write-Host "[-] Error details: $_" -ForegroundColor Red
Write-Host "[*] Retrying WSL shutdown..." -ForegroundColor Yellow
wsl --shutdown
Start-Sleep -Seconds 5
try {
$fileStream = [System.IO.File]::Open($vhdxPath, 'Open', 'ReadWrite', 'None')
$fileStream.Close()
Write-Host "[+] File unlocked successfully." -ForegroundColor Green
} catch {
Write-Host "[-] File is still locked. Please close all WSL terminals and Docker services, then run this script again." -ForegroundColor Red
Exit
}
}
# 5. Create diskpart script and run it
Write-Host "`n[*] Compacting VHDX using diskpart..." -ForegroundColor Cyan
$tempDiskpartScript = Join-Path $env:TEMP "compact_docker_vhd.txt"
$diskpartCommands = @(
"select vdisk file=`"$vhdxPath`"",
"compact vdisk",
"exit"
)
$diskpartCommands | Out-File -FilePath $tempDiskpartScript -Encoding ascii
# Run diskpart (requires admin privileges to select/compact vdisk)
$isAdmin = ([Security.Principal.WindowsPrincipal][Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator)
if ($isAdmin) {
diskpart /s $tempDiskpartScript
} else {
Write-Host "[!] Script is not running as Administrator. Requesting elevation to run diskpart..." -ForegroundColor Yellow
Start-Process powershell -ArgumentList "-NoProfile -ExecutionPolicy Bypass -Command `"diskpart /s '$tempDiskpartScript'; Read-Host 'Press Enter to finish elevation'`"" -Verb RunAs -Wait
}
# Clean up temp file
if (Test-Path $tempDiskpartScript) {
# Using .NET to delete the temp file directly and avoid command shell restrictions
[System.IO.File]::Delete($tempDiskpartScript)
}
# 6. Verify new size
$finalFile = Get-Item $vhdxPath
$finalSizeGB = [math]::Round($finalFile.Length / 1GB, 2)
$savedSizeGB = [math]::Round($initialSizeGB - $finalSizeGB, 2)
# Prepare log entry
$timestamp = Get-Date -Format "yyyy-MM-dd HH:mm:ss"
$logMessage = "[$timestamp] Original: ${initialSizeGB}GB | New: ${finalSizeGB}GB | Saved: ${savedSizeGB}GB | Path: $vhdxPath"
$logPath = Join-Path $PSScriptRoot "reduce-docker-vhd.log"
try {
$logMessage | Out-File -FilePath $logPath -Append -Encoding utf8
} catch {
Write-Host "[-] Could not write to log file: $_" -ForegroundColor Yellow
}
Write-Host "`n========================================" -ForegroundColor Green
Write-Host "[+] Compaction completed!" -ForegroundColor Green
Write-Host "[*] Original size: $initialSizeGB GB" -ForegroundColor Cyan
Write-Host "[*] New size: $finalSizeGB GB" -ForegroundColor Cyan
if ($savedSizeGB -gt 0) {
Write-Host "[+] Space saved: $savedSizeGB GB" -ForegroundColor Green
} else {
Write-Host "[*] No space saved. The virtual disk was already fully compacted." -ForegroundColor Yellow
}
Write-Host "========================================" -ForegroundColor Green
Write-Host "[+] Log saved to: $logPath" -ForegroundColor Gray
Write-Host "`n[*] You can now restart Docker Desktop." -ForegroundColor Cyan
Write-Host "[*] Closing window in 5 seconds..." -ForegroundColor Yellow
Start-Sleep -Seconds 5
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment