Storage health

Free Space Is a Lie: How a Local AI Tool Quietly Spent My SSD's Endurance

Free space barely moved. The file stayed small. SMART said OK. And a local AI tool still wrote a reported 37 terabytes in three weeks — straight through an SSD's endurance. Here's the forensic walk-through, and why your dashboards never flinched.

The short version

  • OpenAI’s Codex CLI was reported to write ~37 TB in ~21 days to a local SQLite log (~/.codex/logs_2.sqlite) via insert-and-prune churn — openai/codex#28224.
  • Free space, file size, and SMART’s health status all looked fine the whole time — none of those glance-able numbers is the write-wear odometer.
  • SSD wear is measured by TBW (terabytes written), a cumulative odometer — not by free space.
  • It’s fixed in Codex v0.142.0+ (bridged-log fix slated for v0.143.0) — update and skim the release notes.
  • Watch your drive’s bytes-written over time, not free space. That’s the number that shows wear.
DISK UTILITY 412 GB free SMART: OK · nothing to see BYTES WRITTEN · 21 DAYS · REPORTED 37 TB ↑ ~640 TB / year the free-space number never moved.
The number you check said 412 GB free and “SMART: OK.” The number you don’t showed a reported 37 TB already written.

I want to ruin a number you trust. It's the one in the corner of your disk utility — the one that says you've got 412 GB free, the one you glance at and think yeah, my drive's fine.

That number is real. It's also almost completely useless for telling you whether something is quietly eating your SSD alive.

Here's the story that made me write this. Start in a terminal, because that's where it actually shows up:

$ ls -lh ~/.codex/logs_2.sqlite*
-rw-r--r--  1 you  staff   210M  logs_2.sqlite
-rw-r--r--  1 you  staff    32M  logs_2.sqlite-wal
-rw-r--r--  1 you  staff    32K  logs_2.sqlite-shm

Nothing alarming, right? A couple hundred megabytes for a log database, a write-ahead log alongside it, the usual shared-memory file. You'd glance at that and move on. I would have too.

Now look at the same database from the inside. On the reporter's machine, the retained row count sat flat at about 680,000 rows, parked. But the table's AUTOINCREMENT id had already marched past 5.5 billion. That's not a typo, and I didn't add a zero. The DB was holding around 680,000 rows while it had handed out more than five and a half billion ids over its life. A gap of more than 8,000x between what it kept and what it had written.

That gap is the entire story. Everything else is just explaining how a number that ugly hides behind a file that small.

What actually happened with Codex

The short version, all attributable to GitHub issue openai/codex#28224: OpenAI's Codex CLI — the local coding agent a lot of us now leave running all day — was found writing absurd volumes to a local SQLite "feedback log" at ~/.codex/logs_2.sqlite. Nothing nefarious, nothing that phones home. Just a log. The reporter, @1996fanrui, opened the issue on June 14, 2026, and the first fixes landed within about ten days.

The headline measurement: about a reported 37 terabytes over roughly 21 days of uptime on his machine. Extrapolate that and you're looking at something like 640 TB a year — call it 640 full overwrites of a 1 TB SSD, annually, from one log file you never asked about and never looked at.

How do you write 37 TB into a file that's a couple hundred megabytes? You don't store it. You churn it. The reporter caught the mechanism with a 15-second sample: in that window, 36,211 rows were INSERTED while the retained count barely moved. Write a batch, prune the old batch, write, prune — forever. The file stays small because old rows get evicted as fast as new ones land. The AUTOINCREMENT id is the giveaway: SQLite hands out a new id for every insert and never reuses one, so 5.5 billion ids against half a million live rows means roughly 5.5 billion inserts churned through a file that, on disk, looks like a modest little database.

The retained row count sat flat at about 680,000. The autoincrement id had passed 5.5 billion. The drive felt every one of them.

Why so much? The log sink defaulted to global TRACETargets::new().with_default(Level::TRACE) — so it persisted everything: dependency internals, raw WebSocket and SSE payloads from the model stream, OpenTelemetry mirror events, the works. Every token streamed back, written to disk. And the part that still makes me wince: it bypassed RUST_LOG entirely. You could set RUST_LOG=warn, the universal Rust "please shut up," feel responsible, and change absolutely nothing. The sink didn't read it. The firehose didn't care about your environment variable.

To be fair to the people who fixed it: they fixed it, fast. PRs #29432 ("Stop logging every Responses WebSocket event") and #29457 ("Filter noisy targets from persistent logs") shipped in v0.142.0, and #29599 ("Stop persisting bridged log events") was merged on June 23 and slated for v0.143.0 — together a reported ~85% cut in log writes. So if you run Codex, update to at least v0.142.0 now, and check the v0.143.0 release notes for the bridged-log fix. (Report-era workaround if you're stuck: symlink ~/.codex/logs_2.sqlite to a RAM-backed /tmp path on macOS or Linux. Windows had no clean fix at the time.) The point of this article isn't "Codex bad." Codex is fine. The point is that a normal, well-meaning local tool spent real hardware endurance for three weeks, and none of the usual gauges flinched.

A small file can still eat your drive

FILE ON DISK 210 MB ↔ flat · free space unchanged WRITES TO FLASH (TBW) 37 TB ↑ INSERT → DELETE → INSERT → DELETE → forever >8,000×written vs. kept
Write a batch, prune it, repeat: the file stays small, but the drive feels every write — a reported 37 TB in 21 days.

Here's the part that breaks people's intuition, and it's worth getting right because it generalizes way past one tool. The instinct almost everyone has — if my disk isn't filling up, nothing's writing much — is just wrong.

The amount of data a file contains and the amount your SSD has physically written are two completely different numbers. A 40 MB log rewritten ten thousand times has cost your flash far more than a 40 GB video you copied once and never touched. The honest yardstick for wear is TBW — terabytes written — a cumulative odometer of every byte the drive has been asked to write. Delete the row a millisecond later and the cell was still programmed; the odometer still ticked.

Three things conspire to hide this. The first is insert-and-prune churn: the file stays small because old rows are deleted as fast as new ones arrive, so the on-disk size is flat while the bytes written is a freight train. Size tells you nothing about throughput.

The second is the one people get backwards. Free space measures net. Endurance is spent gross. You can write a petabyte to a drive whose free-space number never twitches.

The third is write amplification, and it's the sneakiest. A logical 4 KB write often costs more than 4 KB physically — flash erases in big blocks, the controller shuffles live data around (garbage collection), and the filesystem and database each pile on overhead. SQLite's WAL is part of this: a change appends to the -wal file first, then checkpoints back into the main DB, so a single insert can hit the disk more than once. That's amplification in miniature, living under a file that looks tiny in Finder.

So you've got a tool generating a torrent of tiny inserts (bad), through a database that amplifies each one (worse), evicting them immediately so the file never grows (invisible). Every layer conspires to keep the damage off every dashboard you'd check.

For stakes: some consumer 1 TB SSDs are warranted to around 600 TBW. Put that next to "640 TB a year" and you see it — this one log sink could chew through a drive's warranted write endurance in roughly eleven months. TBW is a spec, not a cliff; drives routinely outlive their rating, sometimes by a lot. But it's the number the manufacturer is willing to stand behind, which makes it the honest one to watch. Free space isn't even in the conversation.

Why you almost certainly can't see it

Free space412 GB File size210 MB SMARTOK writes / appover timeno one keeps this
Three green snapshots — and the one view none of them keep: writes per app, over time.

So you go to check. Here's the trap: the instruments macOS hands you are built for snapshots, not history.

The real number — your drive's lifetime bytes written — lives in SMART, as NVMe's "Data Units Written" or SATA's Total_LBAs_Written. You read it with smartmontools:

# brew install smartmontools, then:
smartctl -a /dev/disk0
# NVMe: "Data Units Written" (smartctl prints the TB value in brackets)
# SATA: look for Total_LBAs_Written (that's LBAs — multiply by 512 bytes for the real total)
# The trick: read it now, read it next week, watch it move.

One reading tells you nothing. The delta between two readings tells you everything.

And now the macOS caveat, because I'd rather under-promise: on Apple Silicon, the internal SSD generally doesn't expose this the way external and many Intel drives do. smartctl often can't read the built-in flash on an M-series Mac at all, and self-tests usually aren't available. External USB and Thunderbolt drives and plenty of Intel Macs read fine; the internal drive on a modern MacBook frequently doesn't. So "just run smartctl" is good advice that quietly doesn't work on a lot of the exact machines reading this. Anyone who tells you there's a clean one-liner that always works is selling something.

For catching a live culprit right now, sudo fs_usage -w -f filesys <pid> streams a process's writes in real time, and Activity Monitor's Disk tab shows "Bytes Written" per process. Both are useful. Both are also strictly now — since boot, at best. Quit the app, reboot, and the counter resets; the story's gone. These are stethoscopes, not flight recorders. A process that wrote 30 TB last week and behaves today looks innocent.

Which leaves you with the trap in full: the file looks small (it is), free space looks unchanged (it is), SMART says OK (it does, when you can even read it) — and the drive is worn down the whole time. Three green lights and a problem.

This was never really about Codex

It's tempting to read all this as "Codex bad," close the tab, and move on. That's the wrong takeaway, and it's the dangerous one. Codex got caught because one person got curious enough to crack open the SQLite file and compare the autoincrement id to the row count. That's the entire detection story.

Pull back and the structural problem comes into focus: how many tools like this do you have? Coding agents. IDE assistants re-indexing your whole repo on every save. Local LLM runners. Vector databases re-embedding your notes. Spotlight-style indexers, sync daemons, telemetry buffers, crash loggers, every Electron app with its own little logging habit. We added a whole new class of always-on local software in about two years, and our instinct for spotting when one misbehaves hasn't caught up. Each one reasonable on its own. Collectively, a lot of silent traffic to your flash — and any of them can develop a logging bug, an over-eager cache, or a pathological retry loop with the exact same failure mode: small file, free space unchanged, SMART OK, drive quietly aging.

The disk doesn't fill up. The dashboard stays green. The flash wears out anyway. That's not one tool's bug — it's a blind spot in how we watch our own machines.

The failure here wasn't malice. It was invisibility. The data to notice existed — bytes-written climbs in a straight line — but nothing on the machine was keeping the line.

What I'm building CoreGuard to see

This is the whole reason I started CoreGuard, so I'll keep it concrete — and honest, because after everything above, hand-waving would be hypocritical.

Today, CoreGuard watches the drive-level picture. For your SSD it reads TBW, power-on hours, the raw SMART details, and — the part I care about most — it keeps a wear trend and an "estimated years left" instead of a single snapshot you have to remember to check. You're watching the odometer climb instead of guessing from free space. The SSD health verdict and life-remaining percentage are free, because knowing your drive is in trouble should never cost money; the endurance detail and the trend are part of Pro. (Same honest caveat as above: where Apple Silicon doesn't expose full SMART, CoreGuard shows what your hardware exposes and doesn't pretend otherwise.) It also tells you, in plain English, what's eating your Mac right now — the actual top app or process hogging CPU, RAM, or system load, not just a wall of numbers — and keeps a history of readings with threshold alerts, so "this started three days ago" is a thing you can see instead of a thing you'd have had to be watching live to catch. Per-app energy analytics are in Pro too.

And here's the part I built the app around: CoreGuard watches disk writes per app, over time — a kept history, not just a live snapshot — and flags the anomaly when a process's write rate jumps the way Codex's did. "This app started writing far more than usual three days ago" is exactly the nudge the Codex story needed on day one, and it's the kind of thing CoreGuard puts in front of you. True to the rule that danger is always free, seeing that something is hammering your SSD isn't paywalled — the detailed per-app write timeline, the longer history, and the exportable record are the Pro power tools. It can't promise your drive won't die (nothing honest can), but it turns a silent, invisible write pattern into a trend you can actually watch — and names the process responsible.

CoreGuard is local-only — zero network connections, no account, no telemetry; verify it yourself with lsof -i -nP | grep CoreGuard and watch nothing happen — and it's notarized and Developer-ID signed by Apple. It observes and explains. It never cleans, optimizes, or "speeds up" anything, and it never touches your files. It just shows you what's actually going on.

Here's the math I'd ask you to sit with. CoreGuard observes and explains — it doesn't prevent failures, and no honest tool can promise your drive won't die. But your data and your drive are worth far more than $29. One drive's data-recovery bill — the photos, the projects, the only copy of something you didn't know was the only copy — dwarfs the price of the app many times over. Pro is a one-time $29 (Family $49), perpetual, not a subscription, with a 30-day money-back guarantee. Danger visibility is always free; Pro is the power to act and the portable proof, never the knowledge that something's wrong.

CoreGuard isn't out yet — the download and checkout go live shortly. So this is the honest ask: get notified and grab it free at launch, or see what Pro adds. Either way, go read your bytes-written number. Then read it again next week. Trust the delta, not the free space.

How to check your Mac’s SSD writes

  1. Install the tool: brew install smartmontools.
  2. Read the lifetime counter: smartctl -a /dev/disk0 — on NVMe look for Data Units Written (smartctl prints the human-readable TB in brackets); on SATA, Total_LBAs_Written (multiply by 512 bytes for the real total).
  3. Mind the Apple-Silicon caveat: the internal SSD on M-series Macs often won’t expose this. External, Thunderbolt, and many Intel drives read fine.
  4. Trust the delta: read it now, read it again next week. The change is the story — not the snapshot.
  5. Catch a live culprit: sudo fs_usage -w -f filesys <pid>, or Activity Monitor’s Disk tab → “Bytes Written” per process (since boot only).

Frequently asked questions

Is Codex killing my SSD?

Short answer: not anymore. The reported behavior — roughly 37 TB written in about 21 days to a local SQLite log (openai/codex#28224) — was a logging bug, fixed in Codex v0.142.0 and later (the bridged-log fix is slated for v0.143.0). Update Codex and the worst is handled. The lasting lesson is about watching SSD write wear, not Codex specifically.

How much did Codex write to the SSD?

On the reporter's machine, a reported ~37 TB over about 21 days — extrapolated to roughly 640 TB a year. For scale, many 1 TB consumer SSDs are warranted to around 600 TBW, so a pattern like that could spend a drive's warranted write endurance in roughly eleven months. Those figures are the reporter's measurements in openai/codex#28224, not a universal number.

How do I stop Codex from writing so much to my SSD?

Update Codex to v0.142.0 or later, which fixes the over-logging (the bridged-log fix is slated for v0.143.0). As a report-era stopgap on macOS or Linux you could redirect the log database (~/.codex/logs_2.sqlite) to a RAM-backed /tmp path with a symlink; Windows had no clean workaround at the time.

Is Codex safe to use now?

Yes. Codex was not malicious, and the over-logging is fixed in current builds — update to at least v0.142.0 and check the release notes. Treat it like any always-on local tool and keep an eye on your drive's bytes-written over time.

What is TBW (terabytes written)?

TBW is the cumulative total of every byte ever written to the SSD — a one-way odometer. It is the honest measure of write wear, and manufacturers warrant a drive to a TBW figure (often around 600 TBW for a 1 TB consumer drive). Free space and file size do not reflect it.

What is write amplification?

When a logical write costs more physical flash writes than its size — flash erases in large blocks, the controller relocates live data (garbage collection), and the filesystem and database (for example SQLite's write-ahead log) add overhead. It's a big reason a small, constantly rewritten file can wear an SSD far more than its size suggests.

Does free space show SSD wear?

No. Free space measures net storage used; endurance is spent gross. An app can write a petabyte through a small file it constantly rewrites while your free-space number never moves. Watch bytes-written (TBW), not free space.

How do I check how much my Mac's SSD has written?

Install smartmontools (brew install smartmontools) and run smartctl -a /dev/disk0 — read Data Units Written on NVMe (smartctl prints the human-readable TB in brackets), or Total_LBAs_Written on SATA — then re-read in a week and watch the delta. Note that Apple-Silicon internal SSDs often do not expose this; external and many Intel drives do.

Your data is worth more than $29.

It’s the same math as a $20 case on a $1,500 phone: low-cost visibility into an expensive, silent risk, where a single drive’s data-recovery bill dwarfs the app many times over. CoreGuard observes and explains — it won’t prevent a failure, but it turns an invisible write problem into a trend you can see coming.

launching soon · one-time purchase, not a subscription · 30-day money-back · local-only, zero telemetry

Sources & further reading

Related reading

All insights