Docker環境でゴリゴリ開発していたら、突然「ディスク容量不足」の警告が。調べてみると、WSL2のext4.vhdxが100GB超えてました...。
WSL内でdf -h
してみたら20GBくらいしか使ってないのに、なんでこんなに肥大化してるんだ?と思って調べた結果をまとめます。
TL;DR(結論だけ知りたい人向け)
- WSL2は削除したファイルの領域を自動で解放しない
- PowerShellの
Optimize-VHD
で圧縮できる(DiskPartより簡単) - 100GB→22GBまで削減できた(実使用20GB)
- 月1回の定期メンテナンスで予防可能
まず現状確認:VHDXファイルどこにある?
WSL2のディスクイメージは以下のパスにあります:
C:\Users\<ユーザー名>\AppData\Local\Packages\
CanonicalGroupLimited.Ubuntu<バージョン>_xxxxx\LocalState\ext4.vhdx
PowerShellで一発確認する場合はこちらをpowershellで実行します。
# VHDXファイルのパスを取得
$wslPath = (Get-ChildItem "$env:LOCALAPPDATA\Packages" -Recurse -Filter "ext4.vhdx" | Select-Object -First 1).FullName
# サイズを確認
Get-ChildItem $wslPath | Select-Object Name, @{Name="Size(GB)";Expression={[math]::Round($_.Length/1GB,2)}}
僕の場合、103.67GBありました。やばい。
WSL内の実使用量も確認:
df -h
結果は19.8GB。えっ、差分80GB以上...?
これが「削除したファイルの領域が解放されてない問題」だと判明。
解決方法:PowerShellで圧縮(これが一番楽だった)
最初Googleで調べたら「DiskPart使え」って記事ばかり出てきたんですが、試したらattach vdisk
でエラー連発...。
色々調べた結果、PowerShellの方が圧倒的に簡単でした。
手順1:WSLを完全停止
# WSLをシャットダウン
wsl --shutdown
# 停止確認(全部Stoppedになってること)
wsl --list --verbose
重要: これやらないとデータ破損のリスクあり。必ず停止確認してください。
手順2:圧縮実行
powershell
# VHDXパス取得
$wslPath = (Get-ChildItem "$env:LOCALAPPDATA\Packages" -Recurse -Filter "ext4.vhdx" | Select-Object -First 1).FullName
# 圧縮(Full Modeが効果高い)
Optimize-VHD -Path $wslPath -Mode Full
実行したら5分くらい待って...
手順3:結果確認
# 圧縮後のサイズ
Get-VHD -Path $wslPath | Select-Object @{Name="FileSize(GB)";Expression={[math]::Round($_.FileSize/1GB,2)}}
結果:103.67GB → 22.15GB
削減率78.6%! めっちゃ減った。
DiskPartで失敗した話(参考までに)
最初DiskPartで試したんですが、これが結構ハマりました。
やったこと
wsl --shutdown
diskpart
select vdisk file="C:\Users\...\ext4.vhdx"
attach vdisk readonly
エラー発生
DiskPart にエラーが発生しました: プロセスはファイルにアクセスできません。
別のプロセスが使用中です。
は?wsl --shutdown
したのに...
原因を調査
# WSLプロセスが残ってないか確認
Get-Process | Where-Object {$_.Name -like "*wsl*"}
やっぱりwslservice
が残ってた。強制終了してもダメ。
結局、WSL2はHyper-Vベースだからロックされやすいことが判明。
DiskPartはVHDXを直接操作するから、ロック回避できないっぽい。
結論:PowerShellのOptimize-VHD
の方がWSL2に最適化されてる
定期メンテナンス用のスクリプト作った
毎回手動でやるの面倒なので、スクリプト化しました。
wsl-optimize.ps1:
Write-Host "WSL2ディスク最適化スタート..." -ForegroundColor Green
# WSL停止
wsl --shutdown
Start-Sleep -Seconds 3
# VHDXパス取得
$vhdxPath = (Get-ChildItem "$env:LOCALAPPDATA\Packages" -Recurse -Filter "ext4.vhdx" | Select-Object -First 1).FullName
if ($vhdxPath) {
# 圧縮前サイズ
$beforeSize = [math]::Round((Get-Item $vhdxPath).Length/1GB, 2)
Write-Host "圧縮前: ${beforeSize}GB"
# 圧縮実行
Optimize-VHD -Path $vhdxPath -Mode Full
# 圧縮後サイズ
$afterSize = [math]::Round((Get-Item $vhdxPath).Length/1GB, 2)
$saved = $beforeSize - $afterSize
Write-Host "圧縮後: ${afterSize}GB (${saved}GB削減)" -ForegroundColor Cyan
} else {
Write-Host "VHDXファイルが見つかりません" -ForegroundColor Red
}
タスクスケジューラで週1自動実行:
- タスクスケジューラ起動
- 基本タスク作成
- トリガー:毎週日曜深夜
- 操作:
powershell.exe -ExecutionPolicy Bypass -File "C:\path\to\wsl-optimize.ps1"
これで放置OK。
容量肥大化を予防する設定
WSL内の不要ファイル削除
定期的にこれ実行してます:
# Dockerのゴミ掃除(これが一番効く)
docker system prune -a
# APTキャッシュ削除
sudo apt clean
sudo apt autoclean
sudo apt autoremove
# ログファイル削除
sudo journalctl --vacuum-time=7d
.wslconfigで自動解放を有効化(実験的機能)
C:\Users\<ユーザー名>\.wslconfig
を作成:
[wsl2]
[experimental]
autoMemoryReclaim=gradual
sparseVhd=true
sparseVhd=true
で削除時に領域解放されやすくなる...らしい(まだ実験段階)。
Dockerデータを別ドライブへ
Dドライブに余裕あるなら、移動するのもあり:
sudo systemctl stop docker
sudo mv /var/lib/docker /mnt/d/docker
sudo ln -s /mnt/d/docker /var/lib/docker
sudo systemctl start docker
まとめ:やってよかったこと・分かったこと
即効性あった対処
- PowerShellでの圧縮(DiskPartより簡単・確実)
- Docker定期クリーンアップ(
docker system prune -a
) - 週1の自動メンテナンス(スクリプト化)
分かったこと
- WSL2は削除ファイルの領域を自動解放しない仕様
- DiskPartよりPowerShellの
Optimize-VHD
が安全 - Docker使ってるとめちゃくちゃ肥大化する
- 月1メンテすれば予防できる
トラブったときのチェックリスト
自分用メモ:
wsl --list --verbose
で完全停止してるか確認- PowerShell管理者権限で実行してるか
- それでもダメならPC再起動
よくある質問(自分が疑問に思ったこと)
Q: 圧縮でデータ消えない?
A: 消えない。削除済みファイルの「空き領域」を解放するだけ。
Q: どのくらいの頻度でやればいい?
A: 僕は月1。Dockerヘビーユーザーなら週1推奨。
Q: DiskPartとPowerShellどっち使う?
A: PowerShell一択。DiskPartはWSL2と相性悪い。
Q: 時間かかる?
A: 100GBで5〜10分くらい。寝る前にやってる。
同じ問題で困ってる人の参考になれば。何か質問あればコメントください!