Tech BlogAWSツール & 技術ブログ

Macの空き容量を大きく取り戻した話——Docker・iOSシミュレータ・uvキャッシュという「無言の犯人」たち

このブログの記事はいつも Claude Code を毎日使う開発機(Mac)から書いていますが、ある日「ディスク空き容量が少ない」という通知に気づきました。確認すると空き容量が心もとないところまで減っています。普段そこまで大きなファイルを扱っている自覚がなかったので、何が容量を食っているのか本格的に調査することにしました。

結論から言うと、犯人は開発ツールが無言で溜め込むキャッシュ・ボリューム類でした。Finder の「ストレージ管理」だけでは正体が見えないものがほとんどで、ターミナルでの調査が必須でした。この記事では、実際に効果があった調査コマンドと解放手順を、大まかな傾向つきでまとめます。

調査そのものをClaude Codeに任せた

今回、dusystem_profiler のオプションを1つずつ自分で調べるのではなく、「ディスク容量が逼迫しているので原因を特定してほしい」と Claude Code に調査そのものを依頼しました。状況を伝えるだけで、候補になりそうなディレクトリの当たりをつけて実行し、各ツール(Docker・Xcode・uv)ごとの正しい確認・解放コマンドを提案してくれます。コマンドを検索エンジンで探し回るより明らかに速く、「このコマンドが何をしているか」も併せて確認しながら進められるのが利点でした。

さらに、調査結果は箇条書きのメモではなく、以前この記事で紹介したHTMLドキュメント化の手法でレポートとして出力してもらいました。内訳の比較や対処の優先順位が表として整形された状態で残るため、後から見返すときも、同じような調査を別のMacでやり直すときも、そのまま手順書として使えます。「調査を代行させる」だけでなく「その結果を後から使える形で残す」ところまでセットにすると、この手のトラブルシューティングは一段と効率が上がると感じました。

調査の前提: duとdfが合わないのは仕様

まず最初にハマったのが、du で積み上げたサイズの合計と df が示す実使用量が一致しないことでした。差が出るのは主に次の理由です。

  • APFS のクローン(Copy-on-Write): cp で複製したファイルは実体を共有するため、du で二重にカウントされることがある
  • スパースファイル: 仮想ディスク(後述の Docker.raw など)は「見かけ上のサイズ」と「実際にディスクを占有しているサイズ」が異なる
  • 仮想マウント領域: 特定ディレクトリ配下がマウントポイントになっている場合、du がマウント先の中身まで数えてしまい二重計上になる

この事情があるため、「du の合計を信じて上から順に消す」のではなく、ツールごとに専用の確認・解放コマンドがあるかを個別に調べるのが結局は近道でした。

何にどれだけ使われていたか(大まかな傾向)

調査した範囲で判明した内訳を、大まかな比率で示すとこの通りです(正確な数値ではなく傾向として見てください)。

開発機で容量を食っていた内訳(傾向) Docker Desktop 突出 iOSシミュレータランタイム 中程度 uvキャッシュ 中程度 iOS DeviceSupport(世代分) ※ 上位4項目だけでも空き容量不足の主因と言える割合を占めていた ※ バーの長さは相対的な比率のイメージ(正確な容量比ではない)

以下、それぞれの犯人と対処を順番に見ていきます。

犯人1: Docker Desktop

一番大きかったのが Docker Desktop でした。ここで最初にやりがちな間違いが「docker system prune だけで満足すること」です。

docker system prune -a --volumes

これで未使用のイメージ・コンテナ・ボリュームは消えますが、Docker Desktop が確保している仮想ディスク本体(Docker.raw)は縮小されません。Docker for Mac は Linux VM 用にスパースファイルの仮想ディスクを持っていて、中身を消してもホスト側のファイルサイズはそのままなのです。

実際にホスト側の容量を戻すには、もう一段階必要でした。

  1. docker system prune -a --volumes で中身を整理する
  2. Docker Desktop の Settings → Resources → Disk usage limit を確認し、上限を下げる(あるいは一度下げてから元に戻す)ことで、内部的な qemu-img の再圧縮相当の処理が走り、ホスト側のファイルサイズが実際に縮む

注意点として、ボリュームには開発用データベースのデータなどが入っていることがあるため、--volumes を付けた prune は中身を確認してから実行すべきです。存在するボリューム一覧は docker volume ls で事前確認できます。

犯人2: iOSシミュレータランタイム(数世代分が蓄積)

Xcode を使っていると、iOS シミュレータのランタイムが無言でバージョンを重ねていきます。調査時点で十数バージョン分が残っており、合計するとそこそこの容量を占めていました。

# インストール済みランタイムの一覧
xcrun simctl runtime list

# 不要なランタイムを削除(UUIDを指定)
xcrun simctl runtime delete <UUID>

ここでハマったのが、/Library/Developer/CoreSimulator/Volumesdu で調べても実際の消費量と合わないことでした。このディレクトリはマウントポイントの実体を含むため、du の値がそのまま二重計上になります。実消費を知りたい場合は、xcrun simctl runtime list が表示する Images・Cryptex・Caches の内訳を見るのが正確でした。

シミュレータ関連ではもう1つ、古いシミュレータデバイス自体も溜まっていました。

xcrun simctl delete unavailable

このとき確認したところ、登録されていたシミュレータデバイスの過半数が unavailable(対応するランタイムが既に存在しない孤児デバイス)でした。ランタイムを消す前にデバイス側を先に整理しておくと、依存関係で迷わずに済みます。

副次的に、~/Library/Developer/Xcode/iOS DeviceSupport も実機の OS バージョンごとに数GB単位で蓄積することが分かりました。最新バージョン以外は、実機を接続し直せば再生成されるため削除して問題ありません。

犯人3: uvキャッシュ

Python のパッケージマネージャ uv は、既定でパッケージキャッシュを無制限に溜め込みます。ビルドの高速化のためのキャッシュなので、消しても実害はありません。

uv cache clean

これは完全に安全な操作で、次回以降のインストール時に再ダウンロードされるだけです。効果の割にリスクがほぼゼロなので、他の対処より先に真っ先に試す価値があります。

番外編: コンソールから確認できない領域

もう1つ分かったのが、~/.Trash(ゴミ箱)は Claude Code のようなターミナル経由のツールからは du で調べられないということです。これは macOS の Full Disk Access 権限の制約で、ターミナルアプリ自体に権限を付与していない限り中身が見えません。ゴミ箱がどれだけ容量を圧迫しているか確認したい場合は、素直に Finder 上で確認するのが早道でした。

まとめ: 調査の優先順位

実際にやってみて分かった、費用対効果の良い調査順序をまとめます。

優先度 対象 コマンド リスク
1 uvキャッシュ uv cache clean ほぼゼロ(再ダウンロードされるだけ)
2 孤児シミュレータデバイス xcrun simctl delete unavailable ほぼゼロ
3 古いシミュレータランタイム xcrun simctl runtime delete <UUID> 低(最新版は残す)
4 iOS DeviceSupport(旧バージョン) 手動削除 低(再生成される)
5 Docker Desktop docker system prune -a --volumes + Disk usage limit調整 中(ボリューム中身の事前確認が必須)

dudf の不一致に惑わされず、ツールごとの専用コマンドで実消費を確認する。これが今回の調査で得た一番の教訓でした。同じように「気づいたら空き容量が減っていた」というMacを使っている方の参考になれば幸いです。