A machine running OpenAI's Codex wrote about 37 TB to its main SSD in 21 days of uptime. Process and file-level checks pinned the continuous writer to one place: Codex's local SQLite feedback log at ~/.codex/logs_2.sqlite. At that rate the drive takes roughly 640 TB a year, about 640 full-drive writes on a 1 TB SSD. Many consumer drives are rated near 600 TBW, so Codex alone can burn a drive's warranted write endurance in under a year.
The database looks small. It retains about half a million rows and sits around 1.2 GiB. The SQLite AUTOINCREMENT counter tells the real story: it has advanced past 5.5 billion ids, a roughly 10,000x gap between rows kept and rows ever inserted. In one 15-second sample about 36,211 rows went in while the retained count never moved. Codex inserts a batch, indexes it, writes it to the WAL, then prunes inside the same transaction. The file size stays flat and the disk keeps taking writes, which is why drive wear rather than database growth is the first visible signal.
The cause is a single line. The persistent SQLite sink is attached with a global TRACE default, Targets::new().with_default(Level::TRACE), in both the app-server and the TUI. Every target gets persisted at TRACE, so the database fills with logs no one chose to keep. The default log level, not a stray code path, is what writes the terabytes, and every install ships this way.
What fills the database
The high-frequency rows are dependency noise and raw protocol traffic: inotify events (128,764 OPEN events for ld.so.cache, 23,843 for passwd, more for nsswitch.conf and locale.alias), thousands of tokio-tungstenite poll_read and with_context entries, websocket frame fields, and repeated OpenTelemetry mirror rows from codex_otel.log_only and codex_otel.trace_safe. TRACE alone is 70.7% of retained bytes; the two OTel mirror targets add another 25.3%. Dropping those categories removes about 96% of retained log bytes without turning feedback logging off. Raw websocket and SSE payload bodies are also persisted, and the reporter left them out of the public sample because they can contain private conversation content.
What breaks
A large logs DB does more than wear the SSD. On startup the app-server's SQLite pool can time out (#27741), and a 419 MB logs DB alongside session state has produced 1-to-5-minute cold starts on WSL2 (#28166). One commenter reports that Codex in /goal mode will delete files and folders trying to free space when the disk is full, so a full drive becomes data loss, and a Linux box rebooted while full can fail to log in.
The fixes, and what they miss
Three PRs merged by late June 2026 and, per the reporter's own Codex, cut persisted logs by about 85%. #29432 stops logging every Responses websocket event and #29457 filters noisy targets, both in 0.142.0; #29599 stops persisting bridged log events in 0.143.0. OpenAI says the series of fixes is in the latest shipping desktop app, CLI, and IDE extension.
Not all of it has shipped. A later comment notes 0.144.1 is missing #31789 through #31792, which appear in 0.145.0-alpha.4 and presumably 0.145.0. A separate root cause, tracked in #32496, has Codex continually rewriting models_cache.json and scope_v3.json for roughly another 1.3 TB a year, well under the logging figure but still idle I/O for nothing. Testing with GPT 5.6 Sol High on macOS, one commenter measured unmitigated churn falling from 33.1 to 7.36 inserted ids per second after the upstream fixes, with a local filter taking it to 0.45.
Mitigation
On a build without the fixes, quit Codex and install a trigger that rejects log inserts outright:
sqlite3 ~/.codex/logs_2.sqlite "CREATE TRIGGER IF NOT EXISTS block_log_inserts BEFORE INSERT ON logs BEGIN SELECT RAISE(IGNORE); END;"
That blocks every insert, feedback logging included, but it stops the writes at once. Several people who built working filter patches could not upstream them: GitHub returned "does not have the correct permissions to execute CreatePullRequest" on their accounts, so the real fixes waited on maintainers either way.
Top comments (0)