はじめに
開発用PCの容量がひっ迫してきました。
エクスプローラーで眺めても、これといって巨大なフォルダは見当たりません。ドキュメント類は合計しても数十MB。プロジェクトのソースもせいぜい数GB。それなのにCドライブは残り少ない。
犯人は WSL2の仮想ディスク ext4.vhdx、単体で80.37GB でした。
この記事は、そこから 18GBを回収し、案件のDBデータやリポジトリには一切手を付けずに済ませた までの実作業ログです。同じ症状で困っている方の役に立てば幸いです。
結論を先に書きます。
| 項目 | 作業前 | 作業後 |
|---|---|---|
Ubuntu ext4.vhdx
|
80.37GB | 62.32GB |
| Cドライブ空き | 逼迫状態 | 192.8GB |
所要時間は待ち時間込みで半日ほど。ただし実際に手を動かすのは30分程度です。
なぜ気づけないのか
WSL2のLinux環境は、Windowsから見ると たった1つの巨大なファイル として存在しています。
C:\Users\<ユーザー名>\AppData\Local\Packages\
CanonicalGroupLimited.Ubuntu_79rhkp1fndgsc\LocalState\ext4.vhdx
AppData は隠しフォルダですし、この中身がどれだけ膨らんでいるかはエクスプローラーからは見えません。私も「なんとなく大きいんだろうな」とは思っていましたが、80GBという実数を見て正直ぎょっとしました。
そして最大の落とし穴がこれです。
WSL2は、Linux内でファイルを削除しても、仮想ディスクのサイズが自動で縮まない。
Dockerイメージを消しても、node_modules を消しても、Windows側の空き容量は1バイトも増えません。伸びる一方で、縮むには明示的な操作が必要です。これを知らないと、いくら掃除しても徒労に終わります。
診断:まず数字を取る
いきなり削除に走らず、現状を数値で押さえます。PowerShellで以下を実行してください。
# WSL2仮想ディスクの実サイズ
Get-ChildItem "$env:LOCALAPPDATA\Packages","$env:LOCALAPPDATA\Docker" -Recurse -Filter "*.vhdx" -ErrorAction SilentlyContinue |
Select-Object @{N="GB";E={[math]::Round($_.Length/1GB,2)}}, FullName | Format-Table -AutoSize -Wrap
# Cドライブの空き
Get-PSDrive C | Select-Object @{N="Used_GB";E={[math]::Round($_.Used/1GB,1)}},
@{N="Free_GB";E={[math]::Round($_.Free/1GB,1)}}
WSL側(Ubuntu内)でも確認します。
df -h /
docker system df
私の場合の結果がこちらです。
TYPE TOTAL ACTIVE SIZE RECLAIMABLE
Images 34 22 22.95GB 9.088GB (39%)
Containers 27 7 44.73MB 17.96MB (40%)
Local Volumes 114 12 15.73GB 12.6GB (80%)
Build Cache 238 0 1.216GB 1.216GB
この RECLAIMABLE 列の読み方が、この記事で一番伝えたいポイントです。
⚠️ 最重要:ボリュームには絶対に触らない
上の結果で目を引くのは Local Volumes の 12.6GB (80%) です。数字だけ見れば一番おいしそうに見えます。
ですが、ここは絶対に docker volume prune してはいけません。
Dockerは「コンテナが停止しているボリューム」を機械的に未使用と判定します。しかし実際には、そこに入っているのは各案件の MySQLのデータ実体 だったりします。私の環境では114個のボリュームがあり、複数の顧客案件の検証用DBが含まれていました。
prune した瞬間に全部消えます。しかも静かに、確認もなく。
再構築できるものであっても、マイグレーション流し直し、テストデータ再投入、動作確認と、丸一日は溶けます。容量を10〜20GB空けるために払うコストではありません。
他にも触ってはいけないものを挙げておきます。
-
~/.rvm(5.3GB) — Rubyの実行環境本体。消すと案件が動きません -
~/.rbenv(1.5GB) — 同上 - 各案件のリポジトリ — 言うまでもなく
-
Claude Cowork等の仮想マシンイメージ —
AppData配下に大きめのvhdxがあっても、業務ツールの実体なら対象外
削っていいもの:3つの定番
逆に、消しても自動で再生成されるものが3つあります。今回回収した18GBは、ほぼ全てここから出ています。
① Dockerのビルドキャッシュ(8.70GB)
docker system df # まず現状確認
docker system prune -f # 停止コンテナ・未使用ネットワーク・dangling イメージ
docker builder prune -af # ビルドキャッシュ全削除 ← ここが一番効く
docker builder prune -af だけで 8.70GB 回収できました。238個のキャッシュオブジェクトのうち、最終アクセスが「6ヶ月前」「7ヶ月前」のものがごろごろしていました。
なお docker system prune -a という -a 付きの選択肢もありますが、使っていない全イメージが消えます。社内レジストリやECRからの再pullが発生するため、今回はあえて使っていません。上記2コマンドで十分です。
② VS Code Server の世代ゴミ(12.7GB)
これが個人的に一番の発見でした。
Dev Container や Remote 接続を使っていると、~/.vscode-remote-containers/bin/ 配下にコミットハッシュ名のディレクトリが作られます。VS Codeが更新されるたびに新しいものが増え、古いものは自動削除されません。
私の環境を数えたら 123世代、合計13GB ありました。1つ約93〜118MBなので、塵も積もれば、です。最古は前年11月以前のものでした。
削除は「日付順で新しい3つだけ残す」方針で行います。安全のため、確認と実行を分けます。
# STEP 1: 対象を目視確認(この時点では何も消えない)
cd ~/.vscode-remote-containers/bin
ls -1t | tail -n +4 > /tmp/vscode_del.txt
wc -l < /tmp/vscode_del.txt # 削除対象の件数
ls -1t | head -3 # 残す3つ ← 必ず確認
# STEP 2: 実行
cd ~/.vscode-remote-containers/bin
while read -r d; do rm -rf -- "./$d"; done < /tmp/vscode_del.txt
du -sh ~/.vscode-remote-containers/bin
cd してから相対パス(./$d)で消すのがポイントです。万一変数が空になっても、カレントディレクトリの外に被害が波及しない書き方にしています。
結果、13GB → 278MB。
消した世代のVS Codeで接続しても、その場で自動再ダウンロードされるだけです。設定・拡張機能・コンテナ側のデータには一切影響しません。
③ その他のキャッシュ(お好みで)
yarn cache clean # ~1.4GB
npm cache clean --force # ~1.4GB
# ~/.gradle/caches も1.7GBほどありましたが、消すと次回ビルドが長引くだけなので今回は保留
本番:vhdxを実際に縮める
ここまでで Linux内では約21GB空きました。しかしWindows側の空き容量は1バイトも増えていません。
冒頭に書いたとおり、vhdxは自動で縮まないからです。ここからが本題です。
STEP 1: Docker Desktop を完全終了
タスクトレイのクジラアイコンを右クリック →「Quit Docker Desktop」。
これは必須です。 起動したままだとvhdxがロックされ、圧縮が失敗します。
STEP 2: WSLを停止
wsl --shutdown
実行後、10秒ほど待ってください。 すぐ次に進むとロックが残っていて失敗します。
wsl -l -v # 全て Stopped になっていることを確認
STEP 3: diskpartで圧縮
管理者権限のPowerShellで実行します。
$vhdx = "C:\Users\<ユーザー名>\AppData\Local\Packages\CanonicalGroupLimited.Ubuntu_79rhkp1fndgsc\LocalState\ext4.vhdx"
@"
select vdisk file="$vhdx"
attach vdisk readonly
compact vdisk
detach vdisk
exit
"@ | diskpart
80GBで15〜30分かかります。「100% 完了しました」が順に出て、最後に「正常に圧縮されました」が表示されれば成功です。途中でウィンドウを閉じないでください。
Docker Desktop側のvhdxも同様に処理します。
$vhdx = "C:\Users\<ユーザー名>\AppData\Local\Docker\wsl\data\ext4.vhdx"
# 以下同じ
なぜ diskpart なのか
Optimize-VHD を使う方法や、.wslconfig に sparseVhd=true を書いて自動化する方法も紹介されています。
ただ、sparse VHD については SSD上でホスト側のファイルシステムまで破損させうる という報告が上がっており、案件データを抱えた業務端末では避けたほうが無難だと判断しました。Optimize-VHD は Hyper-V モジュールが必要で、Home エディションでは使えません。
その点、diskpart の attach vdisk readonly → compact vdisk は、読み取り専用でアタッチしてから未使用ブロックを詰めるという、最も枯れた安全な手順です。ファイルは1つも削除されず、Linux側から見た中身は完全に不変です。
ハマりどころ
実際に手を動かして詰まった箇所を残しておきます。
wsl --shutdown が「Unknown command」になる
Ubuntuのプロンプト内で実行していました。wsl コマンドは Windows側のPowerShellで叩くものです。Ubuntu内には wsmancli という同名の別ツールが入っていることがあり、そちらが反応します。
wsl -l -q の出力が文字化けする
出力がUTF-16LEのため、PowerShellがUTF-8として解釈して壊れます。
$env:WSL_UTF8=1
wsl -l -v
これで解決します。
ディストリ名を間違える
Ubuntu-22.04 だと思っていたら、登録名は素の Ubuntu でした。wsl -l -v で正確な名前を確認してから使ってください(WSL_E_DISTRO_NOT_FOUND はこれが原因のことが多いです)。
docker volume ls | wc -l が「8」を返す
Docker Desktopが停止していると、docker コマンドが「見つかりません」というメッセージを出力し、その行数を wc -l が数えているだけです。ボリュームが8個に減ったわけではありません。ここは肝が冷えました。確認するときはDocker Desktopを起動してから行いましょう。
最終結果
| フェーズ | 内容 | 効果 |
|---|---|---|
| 1 | docker system prune -f |
2.64GB |
| 2 | docker builder prune -af |
8.70GB |
| 3 | VS Code Server 123世代削除 | 12.7GB |
| 4 | vhdx 圧縮 | 80.37GB → 62.32GB |
Cドライブの空きは 192.8GB。当初目標の「10GB程度空けばよい」を大きく上回りました。
そして重要なのは、案件のDBボリューム114個、リポジトリ、Ruby/Node環境には一切触れていないことです。消したのは再生成されるキャッシュだけ。
定期メンテナンスとして
一度きりでは、また同じことになります。四半期に1回 のルーチンとして残しておくのがおすすめです。
# ① Ubuntu内
docker builder prune -af
cd ~/.vscode-remote-containers/bin
ls -1t | tail -n +4 > /tmp/vscode_del.txt
ls -1t | head -3 # 残す3つを目視確認
while read -r d; do rm -rf -- "./$d"; done < /tmp/vscode_del.txt
# ② Docker Desktop終了 → PowerShell
wsl --shutdown
# 10秒待つ
# ③ 管理者PowerShellで compact vdisk
禁止事項:docker volume prune は実行しない
日常的には、大きなビルドを回した後に docker builder prune -af を習慣にしておくだけでも、膨張速度はかなり抑えられます。
おわりに
今回の一番の収穫は、空いた容量そのものよりも 「WSL2は自分で縮まない」という性質を掴んだこと でした。
この一点さえ頭にあれば、次に容量が苦しくなっても慌てずに済みます。Linux内で消しただけでは足りない、compact vdisk まで回して初めて完結する。それだけの話なのですが、知らないと延々と原因を探し続けることになります。
複数案件を並行で抱えて開発していれば、キャッシュが溜まるのは必然です。誰の落ち度でもありません。定期的に掃除する習慣だけ持っておけばよい、というのが今回の学びでした。
同じ症状の方の助けになれば幸いです。
※ 環境:Windows 11 / WSL 2.1.5.0 / Ubuntu 22.04.3 LTS / Docker Desktop
※ 作業は自己責任でお願いします。特に削除系コマンドは、実行前に対象を必ず目視確認してください。