app-server: feedback log sqlite (logs_N.sqlite) grows unbounded — ~0.75 GB/day, no retention/rotation
Summary
The app-server's internal feedback/telemetry log database ($CODEX_HOME/logs_<N>.sqlite) grows without bound — no retention, no rotation, no size cap. On a long-running headless deployment (app-server driven 24/7) it reached 9.8 GB in ~10 days (~0.75 GB/day) before we noticed and manually cleared it.
This is distinct from state_<N>.sqlite (actual thread/session state), which stays small and healthy (~106 MB).
Environment
codex-cli0.132.0 (codex app-server --listen stdio://)- Linux x86_64 (musl vendor build), driven continuously by an automation host (frequent short sessions: heartbeats, scheduled jobs, chat).
CODEX_HOMEset to a persistent dir.
Evidence
logs_<N>.sqlite has a single dominant table logs:
1,420,123 rows, ~9.4 GB used (10.47 GB file, 1.08 GB freelist)
schema: id, ts, ts_nanos, level, target, feedback_log_body, module_path, file, line, thread_id, process_uuid, estimated_bytes
Bytes by column — feedback_log_body dominates at 7.3 GB. By target:
5.88 GB n=314,237 codex_api::endpoint::responses_websocket (TRACE)
0.44 GB n=329,301 codex_otel.log_only
0.39 GB n=324,112 codex_otel.trace_safe
0.21 GB n=215,142 codex_api::sse::responses
0.21 GB n= 453 codex_core::client
Level mix: 675,801 INFO + 614,236 TRACE + 73,883 WARN + 23,110 DEBUG + 7 ERROR.
The largest single rows are ~800 KB each — full op.dispatch.compact / op.dispatch.user_input_with_turn_context request/response payloads serialised verbatim into feedback_log_body.
Steady-state growth (one row of the per-day breakdown):
2026-05-26 140,239 rows 0.71 GB
2026-05-27 136,379 rows 0.72 GB
...
2026-06-03 139,209 rows 0.75 GB
The logs_<N> suffix appears to be a schema version, not a size-based rotation — within a version the table grows indefinitely. RUST_LOG does not appear to gate these sqlite writes (no EnvFilter hook found for the feedback-log layer), and the only "bounded" reference in the binary (bounded_feedback_logs AS (...)) is a read-side CTE used when assembling a feedback bundle, not a write-side cap.
Impact
- ~0.75 GB/day ≈ 22 GB/month, ~270 GB/year of pure telemetry on disk for any long-lived app-server deployment.
- Full prompt/response payloads (including large compaction turns) are persisted indefinitely on the local disk — also a data-retention concern.
Expected behaviour
The feedback log should be bounded by default — e.g. a max age and/or max size with automatic pruning/rotation (à la a rolling window), or an explicit CODEX_HOME/config setting to cap or disable it. Ideally TRACE-level full-payload dumps should not be persisted to the on-disk feedback DB by default.
Workaround
logs_<N>.sqlite* is safe to delete while the app-server is stopped — it is recreated empty on next start and only debug/feedback-bundle history is lost. But this requires manual intervention and downtime since the file is held open continuously.
2 Comments
Potential duplicates detected. Please review them and close your issue if it is a duplicate.
Powered by Codex Action
Adding two measurements from a Windows machine, including one that argues against a causal
link I had assumed myself.
Environment
1. Same unbounded growth, smaller scale
~/.codex/logs_2.sqlite reached 1,097.31 MB (verified by file size on disk) and kept growing
across application restarts. Much smaller than the 9.8 GB in the opening report, but the same
shape: no cap, no rotation, no setting I could find.
The growth here coincided with a single long-running task that spawned 116 subagents directly
over 6 days;
agent thread limit reachedoccurred 25 times, and the database held 468 unclosedrelations. Codex's own internal instrumentation reported log aggregation SQL at 18.7 s, DB
connection acquisition at 8.55 s, and 33 s to write 963 TRACE entries — those three numbers are
self-reported by Codex and I have not independently verified them. The only figure I verified
myself is the file size.
2. Measured: zero growth while idle
I wanted to know whether idle clients drive this. On this machine several processes keep the
Codex runtime open at once: one
codex mcp-serverper open Claude Code window, plus a codexprocess embedded in the ChatGPT desktop app.
Measurement window: 116 minutes. Several idle codex clients at the start; the Codex app and
ChatGPT were closed partway through, leaving one idle
codex mcp-server; no tasks issued; nonew session records written. Combined size of logs_2.sqlite, its -wal and its -shm was
6,492,072 bytes at both ends — a delta of exactly 0 over the whole window, which spanned both
configurations. Modification timestamps did advance, so the files are being touched, but no
growth resulted.
Caveat: a WAL reuses already-allocated space, so "size unchanged" is not the same as "no
writes". What I can say is that idling did not grow the database.
3. A/B test: quarantining the log DB did not reproduce a speed-up
I moved logs_2.sqlite / -wal / -shm aside (nothing else changed) and ran the same fixed workload
before and after: disposable working folder, 3 subagents launched in parallel, each asked only
to run
echo okand summarise the result in 1-2 sentences; wall-clock from start until allthree finished; identical prompt and command every run.
Against bands I registered before measuring (improved = median <= 27 s; same level = 35-55 s;
worse = >= 90 s) this lands on "same level". So on this path I could not demonstrate that the
1.1 GB database was what made things slow, even though the numbers did come down.
Caveats, because they matter for how much weight this deserves:
subagents, heavy work, long duration), so DB contention under heavy load is not ruled out
I mention this because the performance reports (e.g. #29177) and this growth report tend to be
read as one story. The growth is clearly real. The causal step from "large log DB" to "slow" is
the part I could not confirm at light load, and I would rather post the negative result than
leave the assumption unchallenged.
Separate observation (not claimed as a defect)
Each subagent also produces its own session record on disk: one run of the test above (parent
plus 3 subagents) creates 4 records, so the 6 runs created 24, and this machine's sessions
directory now totals about 1.7 GB across 1,285 records. I note it only as context for how
expensive high fan-out is in stored records — that directory is my own work history, not a bug.