WSLのディスク容量を圧迫するvhdxファイルを圧縮・管理する方法【Windows 11】

WSLを日常的に使っていると、Cドライブの空き容量がじわじわ減っていくことに気づく。

原因の大半は、WSLが内部で使っている仮想ディスクファイルの肥大化にある。

この記事では、その仕組みと具体的な対処法を整理する。

目次

WSLのストレージが膨らむ原因 vhdx とは

WSL2は、Linuxファイルシステムを vhdx(Virtual Hard Disk Extended) という形式の仮想ディスクファイルに格納している。

vhdxはMicrosoftの仮想ハードディスクフォーマットで、Hyper-Vや仮想マシンでも使われる形式だ。

WSL2のディストリビューションごとに1つのvhdxファイルが作成され、デフォルトでは以下のようなパスに置かれる。

%USERPROFILE%\AppData\Local\Packages\CanonicalGroupLimited.Ubuntu...\LocalState\ext4.vhdx

このvhdxファイルには厄介な特性がある。

ファイルの追加に応じて自動的にサイズが拡張されるが、ファイルを削除しても自動的には縮小されないという点だ。

たとえばDockerイメージを大量にpullして削除した場合、Linux側の使用量は減っていても、Windows側のvhdxファイルは膨らんだままになる。

以下の図は、この仕組みを示したものだ。

Windows 11 ファイルシステム(NTFS) ext4.vhdx(仮想ディスクファイル) 拡張はするが、自動では縮小しない Linux ファイルシステム(ext4) 実際のデータはここに格納 ファイル追加 → vhdx拡張 自動で大きくなる ファイル削除 → vhdx据え置き 自動では縮まない

つまり、WSLを長く使うほどvhdxファイルは一方的に大きくなる。これがCドライブ圧迫の正体だ。

現在のディスク使用量を確認する

対処の前に、まず現状を把握する。

Linux側の使用量を確認

WSLのターミナルで以下を実行する。

# Windowsのターミナルで wsl にログイン
wsl

df -h /

Filesystem Size Used Avail Use% Mounted on
/dev/sdd  1007G  42G  915G   5% /

Usedの列がLinux内で実際に使われている容量

dfはdisk freeの略で、ファイルシステムのディスク使用状況を表示するコマンド。
-hは–human-readableの短縮形で、バイト単位の数値をK/M/G/Tなど人間が読みやすい単位に変換して表示する。これを付けないと512バイトブロック単位の数値が並んで読みづらい。
/は対象のパス指定。ルートファイルシステム(/がマウントされているパーティション)だけを表示する。これを省略すると、マウントされている全ファイルシステムが一覧表示される。WSLの場合はWindows側のドライブ(/mnt/cなど)も出てきてノイズになるので、/に絞っている。

Windows側のvhdxファイルサイズを確認

エクスプローラーでvhdxファイルで右クリックでプロパティで確認するか

PowerShellで以下を実行する。

Get-ChildItem -Path "$env:USERPROFILE\AppData\Local\Packages\*Ubuntu*\LocalState\ext4.vhdx" | Select-Object FullName, @{Name="SizeGB";Expression={[math]::Round($_.Length/1GB, 2)}}

Ubuntuの部分はディストリビューション名に合わせて変更する。Docker Desktopを使っている場合は、以下のパスも確認する。

Get-ChildItem -Path "$env:USERPROFILE\AppData\Local\Docker\wsl\data\ext4.vhdx" | Select-Object FullName, @{Name="SizeGB";Expression={[math]::Round($_.Length/1GB, 2)}}

Linux側の使用量とvhdxファイルサイズの差が大きいほど、圧縮で回収できる容量が多い。

vhdxファイルを圧縮する

本題の圧縮手順に入る。圧縮の効果を最大化するために、先にLinux側の不要データを削除し、その後でWSLを停止してdiskpartで圧縮する、という流れになる。

圧縮前にLinux側で不要データを削除する

WSLのターミナル上で以下を実行する。sudo(管理者権限)が必要になる。

# パッケージキャッシュの削除
sudo apt clean

# 不要パッケージの削除
sudo apt autoremove -y

apt clean/var/cache/apt/archives/に溜まったダウンロード済みの.debファイルを全削除する。パッケージをインストールや更新するたびにここにキャッシュが溜まるが、インストール済みのソフトには影響しない。apt autoremoveは他のパッケージの依存関係としてインストールされたが、今はどこからも依存されていないパッケージを削除する。いずれもリスクはないが、回収できるのはせいぜい数百MB〜1GB程度のことが多い。

sudoのパスワードがわからない場合はスキップしてよい。sudo不要で実行できる確認として、以下でキャッシュの容量を確認できる。

du -sh ~/.cache/*

duはdisk usageの略でファイルやディレクトリの容量を表示するコマンド、-sは合計だけ表示、-hはK/M/Gなど読みやすい単位で表示する。このコマンドは確認だけで何も削除しない。大きなキャッシュがあればrm -rf ~/.cache/pipのように個別に削除できる。

Dockerの不要リソースを削除する

WSLでDocker Desktopを使っている場合、ここが最も効果が大きい。まず使用状況を確認する。

docker system df

SIZEが全体の容量、RECLAIMABLEが回収可能な容量を示す。数十GBになっていることも珍しくない。

削除を実行する前に、残したいコンテナがあれば先に起動しておく。稼働中のコンテナとその関連リソースはpruneの対象から除外される。

docker system prune -a --volumes

-aは稼働中のコンテナが使っていないイメージをすべて削除する。これがないとタグなしイメージだけが対象になる。--volumesは未使用のボリューム(コンテナのデータ保存領域)も削除対象に含める。

確認プロンプトが表示されるので、内容を確認してyを入力する。停止中のコンテナ、そのイメージ、紐づいていないボリュームが削除される。再度docker pulldocker compose upで取り直せるが、ボリューム内のデータ(DBの中身など)は戻らない。重要なデータがボリュームに入っていないか、事前にdocker volume lsで確認しておくとよい。

diskpartでvhdxを圧縮する

不要データの削除が終わったら、WSLを停止して圧縮に入る。

wsl --shutdown

停止を確認するにはwsl -l -vを実行し、STATEの列がすべてStoppedになっていればよい。

PowerShellを管理者権限で起動する。タスクバーのWindowsアイコンを右クリック→「ターミナル(管理者)」で開ける。diskpartを起動する。

diskpart

DISKPART>というプロンプトが表示されたら、以下を順に入力する。

select vdisk file="C:\Users\<ユーザー名>\AppData\Local\Packages\CanonicalGroupLimited.Ubuntu...\LocalState\ext4.vhdx"
compact vdisk
exit

パスは実際のvhdxの場所に置き換える。パスがわからない場合は、別のPowerShellウィンドウで以下を実行すると確認できる。

Get-ChildItem -Path "$env:USERPROFILE\AppData\Local\Packages\*Ubuntu*\LocalState\ext4.vhdx" | Select-Object FullName

select vdiskは対象を選択するだけで何も変更しない。compact vdiskが実際の圧縮処理で、vhdxのサイズに応じて数分〜数十分かかることがある。処理中に電源断やプロセスの強制終了が起きるとvhdxが破損する可能性があるため、ノートPCならACアダプタを繋いだ状態で実行する。

vhdxの保存先をCドライブ以外に移動する

圧縮だけでは根本的に解決しないケースもある。開発で大量のデータを扱う場合、vhdxの保存先自体をDドライブなど別のドライブに移すのが有効だ。

手順は以下の通り。

WSLを停止する。

wsl --shutdown

ディストリビューションをtarファイルにエクスポートする。

wsl --export Ubuntu D:\wsl-backup\ubuntu.tar

既存のディストリビューションを登録解除する。

wsl --unregister Ubuntu

注意:--unregisterを実行すると元のvhdxは削除される。エクスポートが完了していることを必ず確認してから実行する。

別ドライブにインポートする。

wsl --import Ubuntu D:\wsl\Ubuntu D:\wsl-backup\ubuntu.tar

インポート後、デフォルトユーザーがrootになるため、元のユーザーに戻す設定が必要だ。/etc/wsl.confに以下を追記する。

[user]
default=<あなたのユーザー名>

その後WSLを再起動すれば、元のユーザーでログインできる。

移動前後のディスク配置の違いを示す。

移動前 移動後 Cドライブ(NTFS) OS + アプリ + ext4.vhdx ext4.vhdx(肥大化) Dドライブ(空き十分) Cドライブ(NTFS) OS + アプリのみ(軽量) Dドライブ(NTFS) ext4.vhdx(こちらに移動)

この方法ならCドライブの容量を気にせずWSLを運用できる。

スパースファイルの自動回収を有効にする

WSLの最近のバージョンでは、vhdxの未使用領域を自動的に回収する機能が追加されている。.wslconfigで設定することで、手動圧縮の頻度を減らせる。

%USERPROFILE%\.wslconfigに以下を追記する。

[wsl2]
sparseVhd=true

設定後、WSLを再起動する。

wsl --shutdown

sparseVhd=trueを設定すると、vhdxファイルがスパースファイル(実際に使用している領域だけディスクを消費するファイル形式)として扱われ、未使用領域が自動的に解放されるようになる。

ただし、この設定は新規作成されるvhdxに対して適用される点に注意が必要だ。既存のvhdxをスパース化するには、一度エクスポート→アンレジスター→インポートの手順を踏む必要がある。前述の移動手順と同じ要領で、インポート先を同じ場所にすればよい。

Docker Desktopのストレージ管理

WSLでDocker Desktopを使っている場合、Docker専用のvhdxが別途存在し、これが最大の容量消費源になっていることも多い。

Docker Desktopのvhdxは通常以下にある。

%USERPROFILE%\AppData\Local\Docker\wsl\data\ext4.vhdx

Docker側のストレージ節約には以下が有効だ。

対策コマンド効果
未使用リソースの一括削除docker system prune -a --volumes停止コンテナ・未使用イメージ・ボリュームを削除
ビルドキャッシュの削除docker builder prune -aビルド時のキャッシュレイヤーを削除
使用状況の確認docker system df -vイメージ・コンテナ・ボリュームごとの容量を表示

pruneでLinux側のデータを削除した後、前述のwsl --managediskpartでDocker用のvhdxも圧縮すると、大幅に容量を回収できる。Docker Desktopのディストリビューション名はdocker-desktop-dataであることが多い。

wsl --manage docker-desktop-data --compact

定期的なメンテナンスの仕組みを作る

ここまでの内容を踏まえ、実運用で取り入れやすいメンテナンスの流れを整理する。

月に一度程度、以下の手順を実行するだけでストレージの肥大化を抑えられる。

# WSL内で実行:不要データの削除
sudo apt clean && sudo apt autoremove -y
rm -rf ~/.cache/pip ~/.cache/npm

# Dockerユーザーの場合
docker system prune -a --volumes
# PowerShellで実行:vhdxの圧縮
wsl --shutdown
wsl --manage Ubuntu --compact
wsl --manage docker-desktop-data --compact

これをスクリプトファイルとして保存しておけば、ワンクリックで実行できる。

また、sparseVhd=trueの設定を済ませておけば日常的な肥大化は軽減されるが、大量のDockerイメージを扱った後などは手動圧縮を併用するのが確実だ。vhdxのサイズを定期的に確認する習慣をつけておくと、突然Cドライブが逼迫する事態を防げる。

目次