Skip to content

Instantly share code, notes, and snippets.

@Satal
Last active February 6, 2026 06:23
Show Gist options
  • Select an option

  • Save Satal/758320d5b5d9eb51267aedb4371ae174 to your computer and use it in GitHub Desktop.

Select an option

Save Satal/758320d5b5d9eb51267aedb4371ae174 to your computer and use it in GitHub Desktop.
Ubuntu Cleanup Script
#Requires -RunAsAdministrator
<#
.SYNOPSIS
Compresses/shrinks a WSL2 virtual disk (VHDX) to reclaim unused space.
.DESCRIPTION
Cleans up inside WSL, trims free space, shuts down WSL, then compacts
the VHDX file using either Optimize-VHD or diskpart as a fallback.
.PARAMETER SkipCleanup
Skip the internal WSL cleanup step (apt clean, temp files, Docker prune).
.PARAMETER Distro
WSL distribution name to target. Defaults to the default distribution.
#>
param(
[switch]$SkipCleanup,
[string]$Distro
)
$ErrorActionPreference = "Stop"
Write-Host "`n=== WSL2 Disk Compaction ===" -ForegroundColor Cyan
Write-Host "$(Get-Date -Format 'yyyy-MM-dd HH:mm:ss')`n"
# --- Step 1: Locate the VHDX file ---
Write-Host "[1/7] Locating VHDX file..." -ForegroundColor Yellow
$vhdxFiles = Get-ChildItem -Path "$env:USERPROFILE\AppData\Local\Packages" `
-Recurse -Filter "ext4.vhdx" -ErrorAction SilentlyContinue
if (-not $vhdxFiles) {
Write-Error "No ext4.vhdx file found. Is WSL2 installed?"
exit 1
}
# If multiple VHDX files found, let user know
if ($vhdxFiles.Count -gt 1) {
Write-Host " Found multiple VHDX files:" -ForegroundColor DarkYellow
$vhdxFiles | ForEach-Object {
Write-Host " $($_.FullName) - $([math]::Round($_.Length/1GB,2)) GB"
}
Write-Host " Processing all of them...`n"
} else {
Write-Host " Found: $($vhdxFiles[0].FullName)" -ForegroundColor Gray
}
$beforeSizes = @{}
foreach ($vhdx in $vhdxFiles) {
$sizeGB = [math]::Round($vhdx.Length / 1GB, 2)
$beforeSizes[$vhdx.FullName] = $sizeGB
Write-Host " Current size: $sizeGB GB" -ForegroundColor Gray
}
# --- Step 2: Clean up inside WSL (while it's running) ---
if (-not $SkipCleanup) {
Write-Host "`n[2/7] Cleaning up inside WSL..." -ForegroundColor Yellow
$wslCmd = if ($Distro) { "wsl -d $Distro" } else { "wsl" }
$cleanupScript = @"
sudo apt autoremove -y 2>/dev/null && sudo apt clean 2>/dev/null
sudo rm -rf /tmp/* 2>/dev/null
sudo journalctl --vacuum-time=3d 2>/dev/null
if command -v docker &>/dev/null; then docker system prune -af --volumes 2>/dev/null; fi
echo 'Cleanup complete'
"@
if ($Distro) {
wsl -d $Distro -- bash -c $cleanupScript
} else {
wsl -- bash -c $cleanupScript
}
Write-Host " Done." -ForegroundColor Gray
} else {
Write-Host "`n[2/7] Skipping cleanup (SkipCleanup flag set)" -ForegroundColor DarkGray
}
# --- Step 3: Zero out free space with fstrim ---
Write-Host "`n[3/7] Trimming free space (fstrim)..." -ForegroundColor Yellow
if ($Distro) {
wsl -d $Distro -- sudo fstrim / 2>$null
} else {
wsl -- sudo fstrim / 2>$null
}
if ($LASTEXITCODE -eq 0) {
Write-Host " Done." -ForegroundColor Gray
} else {
Write-Host " fstrim not supported on this build - skipping (compaction will still work)." -ForegroundColor DarkYellow
}
# --- Step 4: Shut down WSL ---
Write-Host "`n[4/7] Shutting down WSL..." -ForegroundColor Yellow
wsl --shutdown
Start-Sleep -Seconds 3
# Verify shutdown
$running = wsl -l -v 2>&1 | Select-String "Running"
if ($running) {
Write-Host " Waiting for WSL to fully stop..." -ForegroundColor DarkYellow
Start-Sleep -Seconds 5
wsl --shutdown
Start-Sleep -Seconds 3
}
Write-Host " WSL stopped." -ForegroundColor Gray
# --- Step 5: Compact the VHDX ---
Write-Host "`n[5/7] Compacting VHDX file(s)..." -ForegroundColor Yellow
$hasOptimizeVHD = Get-Command Optimize-VHD -ErrorAction SilentlyContinue
foreach ($vhdx in $vhdxFiles) {
Write-Host " Processing: $($vhdx.FullName)" -ForegroundColor Gray
if ($hasOptimizeVHD) {
Write-Host " Using Optimize-VHD..." -ForegroundColor Gray
Optimize-VHD -Path $vhdx.FullName -Mode Full
} else {
Write-Host " Optimize-VHD not available, using diskpart..." -ForegroundColor DarkYellow
$diskpartScript = @"
select vdisk file=""$($vhdx.FullName)""
attach vdisk readonly
compact vdisk
detach vdisk
"@
$tempFile = [System.IO.Path]::GetTempFileName()
$diskpartScript | Out-File -FilePath $tempFile -Encoding ASCII
diskpart /s $tempFile
Remove-Item $tempFile -Force
}
}
Write-Host " Compaction complete." -ForegroundColor Gray
# --- Step 6: Verify results ---
Write-Host "`n[6/7] Verifying results..." -ForegroundColor Yellow
$totalSaved = 0
foreach ($vhdx in $vhdxFiles) {
# Re-read the file info after compaction
$updatedFile = Get-Item $vhdx.FullName
$afterGB = [math]::Round($updatedFile.Length / 1GB, 2)
$beforeGB = $beforeSizes[$vhdx.FullName]
$savedGB = [math]::Round($beforeGB - $afterGB, 2)
$totalSaved += $savedGB
Write-Host " $($vhdx.Name):" -ForegroundColor Gray
Write-Host " Before: $beforeGB GB" -ForegroundColor Gray
Write-Host " After: $afterGB GB" -ForegroundColor Green
Write-Host " Saved: $savedGB GB" -ForegroundColor Green
}
if ($vhdxFiles.Count -gt 1) {
Write-Host "`n Total saved: $([math]::Round($totalSaved, 2)) GB" -ForegroundColor Green
}
# --- Step 7: Test WSL ---
Write-Host "`n[7/7] Testing WSL..." -ForegroundColor Yellow
if ($Distro) {
$testResult = wsl -d $Distro -- bash -c "echo 'WSL is working' && df -h / | tail -1"
} else {
$testResult = wsl -- bash -c "echo 'WSL is working' && df -h / | tail -1"
}
Write-Host " $testResult" -ForegroundColor Gray
Write-Host "`n=== Complete! ===" -ForegroundColor Cyan
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment