Codex は SSD を書き潰すのか?
手元のログを調べてみた
朋友圈でこんな警告が回ってきた:Codex はバックグラウンドで logs_2.sqlite に高頻度で書き込み、ファイルは大きく見えなくても、実際の書き込み量はかなり多い可能性がある。最初は大げさな見出しだと思っていたが、手元のログを調べたあと判断を変えた。
2026-06-24 更新:OpenAI はこの bug を認めて修正をマージし、#28224 は一度クローズされた――しかしすぐに再オープンされた。理由は、最新安定版 0.142.0 でもまだ完全には直っていないためだ。完全な修正は同日にリリースされた 0.143-alpha のみに入っている。手元で 0.142.0 にアップグレードしたあとも 3 回の実測でまだ書き込みが続き、複数プラットフォームのユーザーも再現しており、OpenAI 自身の修正 commit でも原因が明示されている――詳しくは後述の「『バージョンを上げれば直る』?」の節を参照。
①ネットでは何が言われているのか
私が見た主張は 2 種類ある。1 つはスクリーンショットの転載で、Codex がディスクを消耗させているとだけ言うもの。もう 1 つは lencx によるソースコードと issue の検証だ。
転載版の主張はこうだ:Codex CLI を入れると、~/.codex/logs_2.sqlite に高頻度でデータを書き込む。ファイルは大きくなっていないように見えるが、SSD の書き込み量は増えている。
証拠版は lencx(浮之静)の 『Codex 日志正在燃烧你的 SSD』 によるものだ。彼は Codex のソースコードと一連の GitHub issue を調べ、問題を SQLite ログ層のデフォルト設定に向けている:TRACE レベルのネットワークイベント、テレメトリのミラー、依存ライブラリのノイズが永続化データベースに書き込まれており、ファイルサイズは実際の書き込み量を表さない。
私はこの方向で自分のマシンを調べた。以下では手元のコマンドと確認可能な issue だけを載せ、伝聞には頼らず結論を出す。
②自分のマシンで何が測れたか
まずファイルを見る:
$ ls -lh ~/.codex/logs_2.sqlite*
logs_2.sqlite 126M
logs_2.sqlite-wal 4.9M
logs_2.sqlite-shm 32K
DB 内の 66% は TRACE。内容は業務ログではなく、websocket のポーリング読み書きに関する低レベルの計測ログだ:
$ sqlite3 ~/.codex/logs_2.sqlite "SELECT level,count(*) FROM logs GROUP BY level ORDER BY 2 DESC"
TRACE 65744 -- 66% を占める
INFO 17968
DEBUG 13459
WARN 1974
ERROR 8 -- 当時の合計は約 9.9 万行
最新の数行はこんな感じ:
TRACE log WouldBlock
TRACE log tokio-tungstenite/compat.rs:157 Read read -> poll_read
TRACE codex_api::endpoint::responses_websocket ...
現在の行数だけ見ても不十分だ。この DB は書きながら削除もするため、サイズにだまされる。
書いているのはデスクトップ版で、あなたが叩いた CLI ではない
元投稿では問題を「CLI が書いている」と説明していた。私はログ内の process_uuid を調べ、直近 500 行がすべて同じプロセスから来ていることを確認した:
$ ps -p 2950 -o command
/Applications/Codex.app/Contents/Resources/codex app-server
書き込み元は Codex デスクトップ版(Codex.app)の app-server だった。当時、私は対話式の codex ターミナルを開いていなかった。より正確に言えば、「CLI がディスクを消耗させている」のではなく、「起動中の Codex プロセスがこの共有 SQLite ログ層に書き込む」ということだ。デスクトップ版、TUI、エディタ拡張のいずれも関与しうる。
ファイルは 126 MB しかないが、10 日で 845 万行を書いていた
主キーは自動増分の id だ。現在の id 範囲と実際の行数を調べてみた:
$ sqlite3 ~/.codex/logs_2.sqlite "SELECT min(id), max(id), count(*) FROM logs"
min(id) 206,551,383
max(id) 215,003,791 -- 自動増分はすでに 2.15 億に到達
count(*) 101,406 -- だが残っているのは 10 万行だけ
範囲の差は 845 万、実際に残っているのは 10 万 => 835 万行はすでに削除されている
これは「挿入-刈り込み」ループだ:テーブルには直近約 10 日分の行だけが残る(私の環境では最古の 1 行がちょうど 10 日前だった)ため、古い行は削除される。ls でファイルが 126 MB に安定して見えることは、書いていないことを意味しない。自動増分 id が示すとおり、10 日間で実際には 845 万行が挿入され、そのうち 835 万行が削除されていた。
ここで私の最初の判断は覆った。もともと私は「残っている 10 万行」で見積もり、「約 7 MB/日」だとして、これは釣り見出しだとも言っていた。実際に挿入された 845 万行で計算すると、ログ本文だけで 約 600 MB/日 になる。私は 90 倍近く少なく見積もっていた。しかもこれは論理書き込みにすぎない;WAL、4 つのインデックス、checkpoint、削除処理そのものもさらにディスクへ書き込む。
上流 issue の規模はさらに大きい:#17320 では、ストリーミング出力時に WAL 書き込みが 約 5–16 MiB/s と測定され、50 token の応答 1 回で MAX(id) が約 5000 行跳ね上がっていた;#28224 の報告者は、21 日で約 37 TB、外挿すると約 640 TB/年を書いた と述べている。私の「6 秒で 109 行しか増えなかった」というサンプリングは、アイドル時間帯だけを捉えていた;高書き込みの時間帯はモデル出力時なのだ。
「バージョンを更新すればいい」? そう単純ではない(2026-06-24 確認)
この一文は分けて見る必要がある。OpenAI はたしかに 0.142.0(2026-06-22 リリース)で 2 つのノイズ低減 PR(#29457 など)をマージし、#28224 は一度クローズされた。だがすぐに再オープンされた(issue の state_reason は今 reopened になっている)——修りきっていなかったからだ:macOS、Windows、Linux のいずれでも、0.142.0 にアップグレード後もなお書き込みが続くという報告があり、ある人は iotop で ~10 MB/s、約 315 TB/年 を実測している。
私のこのマシンでも再現した。CLI とデスクトップ版コアの両方を 0.142.0 に上げたあとも、アイドル時に ~18 行/秒、ストリーミング出力時には数千行まで跳ね、書かれているのは相変わらず TRACE/log、TRACE/hyper_util などだった。なぜ中途半端なパッチは効かないのか? OpenAI は本当の修正 commit #29599 の中で自らこう書いている:
「
0.142.0のディスク書き込みフィルタはtarget=logを無効化するが、依存ライブラリのログ記録はそれらの元の依存 target に基づいてフィルタされるため、tracing-log がそれらをtarget=logとして貼り直す前にすり抜けてしまう——その結果、高頻度な依存 TRACE が引き続き SQLite に入っていた。」
この完全な修正はどの安定版にも入っておらず、2026-06-24 に出た 0.143.0-alpha にだけ入っている。今回の確認時点で、#28224 と #17320 はどちらもまだ Open だ。したがって正確な言い方はこうだ:上流はノイズを減らしたし、完全修正も進行中だが、現在の最新安定版に上げても修ったとは言えない。
③結局、本当なのか嘘なのか
一つずつ照合する:
| 主張 | 実測 | 判定 |
|---|---|---|
こっそり logs_2.sqlite に書いている |
実在する、126 MB | 真 ✓ |
| 「数万件」の無駄ログを書いた | 過小評価だった。10 日で 845 万行 挿入、66% が websocket TRACE | 真(それ以上)✓ |
| 何もしていなくても書いている | おおむね事実。CLI を開いていなくても、バックグラウンドのデスクトップ版 app-server はなお書いていた;Codex プロセスが立っている限り、websocket ポーリングが TRACE を出し続ける可能性がある | 真 ✓ |
| ファイルは大きくなっていないように見えるが、下層では激しく書いている | まさに病巣そのもの。ローリング削除でサイズは安定するが、物理書き込みは止まっていない | 真 ✓ |
| 1 年であなたの SSD を焼き切る | 使用量次第。ヘビーユーザーは対処すべき;ライトユーザーは恐れる必要はない | 誇張 ≈ |
病根はどこにあるのか(lencx が掘ったソースコード)
app-server と TUI は起動時にどちらも SQLite ログ層をぶら下げており、デフォルトフィルタはこうだ:
.with_filter(Targets::new().with_default(Level::TRACE))
つまり、このディスク書き込み sink はデフォルトで TRACE を受け取り、しかも独自のフィルタチェーンを持っているため、RUST_LOG=warn には縛られない。#17320 の報告者はすでに RUST_LOG=warn を設定していたが、logs_2.sqlite にはなお TRACE が出続けていた。この issue は 2026-04-10 に開かれ、確認時点でもまだ Open だった。
SSD リスクは書き込み量次第
SSD の TBW は保証書き込み量であって、そこまで書いたら壊れるという死線ではない。リスクは使用強度、ディスク容量、残り空き容量に依存する:
- ヘビーユーザー:#28224 のように 640 TB/年なら、はんだ付けされた 256 GB Mac SSD(公称約 150 TBW)では数か月で保証書き込み量を超える。このケースは対処に値する。
- ライトユーザー:書き込み量が 1〜2 桁低ければ、ディスクはまだ何年も使えるので、恐れる必要はない。
もう一つ誤解がある:これは同じ小さな領域を「焼き抜く」わけではない。SSD には FTL とウェアレベリングがあり、同じファイル名に書くことは、同じ一群のフラッシュセルを繰り返し消去することと等しくない。本当に見るべきなのは総ホスト書き込み量と、ディスクが満杯に近いときのライトアンプリフィケーションだ。
さらにプライバシーリスクも見落とされやすい:lencx は、複数の issue でこのライブラリが websocket/SSE の生 payload をディスクに落とす可能性が言及されていると指摘している。つまり会話断片が混じる可能性がある。だから logs_2.sqlite を「無害なキャッシュ」として気軽にアップロード、バックアップ、公開してはいけない。
私の結論:仕組みは本物で、上流にも根拠があり、自分のマシンでもなお再現できる;ただしこれは「全員が 1 年で SSD を焼き切る」ものではない。どの Codex プロセスを起動しているか、1 日にどれだけ使うか、マシンの SSD 容量と空き容量に左右される。私は初版で残留行数だけを見て FUD と判定したが、それは間違いだった。
④どうすればいいか
以下の多くは lencx の振り返りに基づくもので、私のマシンでも確認した。作業前にまず Codex を完全終了すること。そうしないと #22444 のハンドル問題を踏む:プロセスが削除済み WAL を掴んだままで、du では減ったように見えても、df では空き容量が戻らない。
まずプロセスが掴んでいないことを確認
pgrep -fl 'Codex|codex|app-server'
lsof -nP | grep -E '\.codex/.+logs_2\.sqlite'
クリーンアップして容量を回収する(対症療法)
# Codex が終了済みであることを確認してから
sqlite3 ~/.codex/logs_2.sqlite 'DELETE FROM logs; VACUUM;'
これでファイルは 126 MB から数 MB まで縮むが、再び書き始めることは止められない。クリーンアップ ≠ レート制限。
一時的な止血:新規書き込みをブロックする(対症療法だが効く)
sqlite3 ~/.codex/logs_2.sqlite \
"CREATE TRIGGER IF NOT EXISTS block_log_inserts
BEFORE INSERT ON logs BEGIN SELECT RAISE(IGNORE); END;"
# ロールバック
sqlite3 ~/.codex/logs_2.sqlite "DROP TRIGGER IF EXISTS block_log_inserts;"
トリガーはすべての挿入を無視するので、効果は直接的だ。代償として feedback_log_body が空になり、バグ報告時に診断用の添付が欠ける。アップグレードでテーブルが再作成されると、トリガーが削除される可能性があるため、アップグレード後に再確認が必要だ。
データベースを別のディスクへ移す(暫定策)
# ~/.codex/config.toml
sqlite_home = "/Volumes/ExternalDisk/codex-sqlite"
# または一時的に: CODEX_SQLITE_HOME=/Volumes/ExternalDisk/codex-sqlite codex
これは単一ファイルをシンボリックリンクするよりきれいだが、書き込み先を移すだけで、書き込み量は減らない。外付けディスクが切断されたりスリープしたりすると Codex に影響する。これは SQLite home 全体を移すため、state、logs、goals、memories もすべて一緒に移動する点に注意。
踏まないほうがいい3つの罠
/tmpのシンボリックリンクは macOS では役に立たない:macOS の/tmpは/private/tmpを指し、APFS のシステムディスク上にあり、メモリディスクではない。そこへシンボリックリンクしても、SSD 上のあるディレクトリから別のディレクトリへ移すだけで、書き込みにはまったく効かない。readlink /tmp; mount | grep -i tmpfsで自分で確認できる。RUST_LOG=warnではこの sink は抑えられない:stderr は減らせるが、この SQLite へのディスク書き込み層には独立したフィルタチェーンがある(#17320 参照)。[feedback] enabled = falseはスイッチではない:これはフィードバック送信フローを止めるだけで、起動時には相変わらずログ層が接続される。
ついでに:バックアップから WAL/SHM を除外する
--exclude='*.sqlite-wal'
--exclude='*.sqlite-shm'
WAL と SHM は実行時ファイルで、数十 GB を別のマシンへ同期しても、たいてい問題をコピーしているだけだ。
上流側ではまだ絞り込みが必要
これらはいずれも止血にすぎない。Codex は、ディスク書き込み sink のデフォルトレベルをさらに絞り、websocket の生 payload をデフォルトで落とさず、WAL と総サイズに上限を設け、明確なスイッチ(たとえば sqlite_log_level)を提供する必要がある。#28224 はノイズ低減 PR によりクローズ済みだが、確認時点では #17320、#22444、#24275、#28997 はまだ Open だった。
自分で1分かけて対処すべきか判断する:まず ls -lh ~/.codex/logs_2.sqlite* でサイズを見て、次に SELECT max(id),count(*) FROM logs を一度実行する。もし max(id) が数千万から億単位なのに、行数が十数万しかないなら、そのマシンでも「書いては消し、消しては書き」をやっている。ヘビーに使っていて、しかも SSD がはんだ付けされた Mac なら対処を勧める。たまに使う程度なら、この件を知っておけば十分だ。

WeChat Pay
Alipay
コメント
コメントは即時公開されますが、ポリシー違反時は非表示になる場合があります。