- date
- entry
- 044
- topic
- tooling
- rev
- —
codex exec generates a 1.7 MB image, and ~/.codex stores about 8 MB
This month a batch of article images was generated with codex exec. I assumed it left nothing behind except the message written by -o; ~/.codex/sessions had grown to 6.1 GB. Matching image by image, most generated images exist four times on disk: one PNG and three base64 copies.
codex exec is Codex's non-interactive mode: you give it a prompt and it exits when it's done. -o names a file for the final reply. I'd been treating it like any other command-line tool, assuming nothing else stayed behind.
But around noon on October 15 this Mac was down to about 2.5 GB free (for how I read free space, see the note on df's Capacity), so I measured ~/.codex. At 12:42 its sessions directory held 1,877 files and 6,144 MB, the oldest retained one from May 3. This note is about what each codex exec run leaves behind, and why the runs that generate images are so large. In order: how I measured, what's in October's 4.6 GB, where one image is stored, what --ephemeral skipped in my tests, and what I haven't tested.
Method: the scripts report only structure and lengths
Session files are JSONL (one JSON record per line), named like rollout-2026-10-15T12-38-28-….jsonl and filed by date in directories such as sessions/2026/10/15/. They contain prompts and tool output, so a Python script read and parsed them and reported only three things: originator from the session_meta on the first line (codex_exec means it came from codex exec), lengths summed by each line's type, and SHA-1 comparisons of the image fields. I didn't look at or print any conversation or image content myself.
MB below means 106 bytes. File sizes are bytes; field lengths are character counts, and since base64 is ASCII, characters equal bytes there, while fields containing Chinese come out a little short, which I note where it matters. Unless another time is given, figures are from a snapshot around 12:15 on October 15.
What's in October's 4.6 GB
From October 1 to about 12:15 on October 15, sessions/2026/10 had 1,326 files, 1,162 of them from codex_exec, totalling 4,606 MB. Median file size 4.43 MB, 90th percentile 5.61 MB, largest 33.5 MB. The heaviest day was October 1: 342 exec sessions, 1,801 MB.
Split by record type, two kinds dominate:
Extensionitems inevent_msg/item_completedwithkindimage_gen.generation, whoseresultfield is the generated image in base64: 865 images, 1,935 MB.input_imageentries inresponse_item/custom_tool_call_output, the images a tool hands back to the model: 1,077 entries, 2,021 MB.
Together that's 3,956 MB, about 86% of the total bytes in the 1,162 files. The lines holding command-execution records (CommandExecution items) total about 297 MB, with the same output stored in both the stdout and aggregated_output fields (identical in all 6,588), but next to the images that's small change. Of the 1,162 exec sessions, 845 generated an image, and those 845 files total 4,128 MB. In other words, most of the bytes in this month's session files are the images that batch of jobs generated.
Where one image is stored
What are those 1,077 input_image entries? Hashing each one's base64 and comparing it with the images generated in the same session, 859 are byte-identical, matching 850 generated images: once generated, the image is handed to the model again as-is, and both copies are written to the session file.
There are two more copies outside the session file. Every generation item's savedPath points to a PNG under ~/.codex/generated_images/ (899 files, 1,524 MB by modification time in October). Across all retained sessions there are 899 savedPaths (873 in the October directory, 26 in September's); all still exist, and the decoded base64 matches the PNG in every case. The other copy is in ~/.codex/thread_history_1.sqlite: this SQLite database, 2.69 GB at the time, has a thread_items table whose 899 imageGeneration items hold about 2,034 MB of JSON, and those also decode to their PNGs.
So how many copies? Counting the 865 generated images one by one: 842 have exactly four (the PNG, two in the session file, one in the database), 15 were never handed back to the model and have three, and 8 were handed back more than once and have five or more. Taking just the 842 with exactly four copies, the PNG averages 1.68 MB (the 1.7 MB in the title) and the four copies together 8.40 MB, about five times the PNG. base64 is roughly a third larger than the original; store it three times and that's the result. That's content in files and fields; how much the disk actually grows per generated image, I haven't measured separately.
This isn't history. Between 12:35 and 12:42, five more exec sessions were written to sessions/2026/10/15/ on this machine, 4.2 to 4.8 MB each.
What --ephemeral skipped in my tests
codex exec --help (codex-cli 0.159.2) gives this flag one line: Run without persisting session files to disk. In a newly created test directory outside git I ran a one-word exec twice:
codex exec --ephemeral --skip-git-repo-check -s read-only "Reply with exactly: ok" < /dev/null
First run, before and after: files under sessions 1,872 → 1,872; history.jsonl and session_index.jsonl unchanged in size; no row in state_5.sqlite's threads table with that directory as cwd; no new rows in logs_2.sqlite's logs table during those 7 seconds. For the second run I ran touch marker first and afterwards find ~/.codex -type f -newer marker: zero newer files under sessions.
find also listed a few modified SQLite and cache files. At the time I counted 37 lines with codex processes in ps, other jobs were running, and I can't attribute those changes to my two runs. What I can say is that in the places I checked, these two --ephemeral runs left no record. The cost is the same fact: with no session file, there's nothing for codex exec resume to resume for these runs.
What I haven't tested, and what I do now
The numbers above are what I know. Two things I haven't tested: whether image generation with --ephemeral still writes a PNG under generated_images, and how the Codex app and thread_history_1.sqlite react if old session files are deleted by hand. The first needs a separate image-generation test; the second touches data other jobs are using. Both are left alone for now.
What I do now: one-off codex exec runs (fact checks, reviews, image checks) always get --ephemeral, because I never resume them and only want the -o output. For jobs that generate images, I run du -sk on ~/.codex/sessions, ~/.codex/generated_images and thread_history_1.sqlite (with its -wal file) before and after, and check that the growth is on the order of a few MB per image; with other jobs running at the same time, that number is only a rough guide. I haven't deleted the old files yet.
If you remember one thing
By default codex exec writes the run to ~/.codex/sessions; on this Mac most generated images are stored twice in the session file and once more in the thread_history database, four copies counting the PNG. 86% of the bytes in October's exec sessions are base64 images. Add --ephemeral to runs you won't resume, and when generating images, I estimate about 8 MB per image (content size; I haven't measured actual disk growth per image).
Revision history
- First published