I plugged the external drive in on a Sunday and looked at the Time Machine sidebar. The bar showed 3.94 TB used on a 4 TB volume. The MacBook I was backing up has a 512 GB SSD and roughly 380 GB of it is used. The backup was almost eight times larger than the source disk, and it was one weekly snapshot away from failing with the ancient "Time Machine could not complete the backup" alert.
I have watched the same story play out on friends' machines to know the shape of it. A dev Mac generates enormous churn in a handful of folders, Time Machine dutifully copies every changed file every hour, and after a year of hourly plus daily plus weekly snapshots stacked on top of each other, the backup drive is a museum of stale caches.
Why does a 512 GB MacBook produce a 4 TB Time Machine backup?
Time Machine keeps a rolling history: hourly snapshots for the last 24 hours, daily for the last month, weekly for every prior week until the drive is full. Hardlinks make unchanged files free, so on a Mac that mostly does Safari and email, the whole history costs maybe twice the size of the source disk.
A developer Mac breaks that model. The files hardlinks save are the ones that never change. On a dev Mac, the biggest folders change constantly, every build, every npm install, every AI turn.
Every Xcode rebuild writes new files under ~/Library/Developer/Xcode/DerivedData/<hash>/. Every Docker container start touches ~/Library/Containers/com.docker.docker/Data/vms/0/data/Docker.raw. Every AI turn writes a fresh transcript into ~/.claude/projects/<hash>/ and appends to ~/.claude/debug/*.log. Every Cursor re-index rewrites a .pack file under ~/Library/Application Support/Cursor/User/workspaceStorage/.
Time Machine sees changed files, so it copies them. Next hour, again. Twelve months of that stacks up.
Which dev caches churn hardest under Time Machine?
I ran a tmutil compare -s between the newest two hourly snapshots on the backup drive and sorted the delta by path. Here is what showed up on top on a fairly ordinary dev Mac.
| Path | Typical daily churn | What's in it | Reproducible from source? |
|---|---|---|---|
~/Library/Developer/Xcode/DerivedData/ |
2 to 6 GB per active project | Build products, module cache, index | Yes, xcodebuild rebuilds |
~/Library/Containers/com.docker.docker/Data/vms/0/data/Docker.raw |
100 MB to full-image size | Sparse VM disk for Docker Desktop | Yes, docker pull rebuilds |
~/.claude/debug/ |
200 MB to 4 GB per heavy Claude Code day | Raw stdout/stderr per session, MCP wire logs | No, but pure diagnostic |
~/.claude/projects/ |
50 MB to 800 MB | Session transcripts | State, but rebuilt on next turn |
~/Library/Caches/claude-cli-nodejs/*/--mcp-logs-* |
Up to 4 GB | Per-connection MCP wire logs | No, pure diagnostic |
~/Library/Application Support/Cursor/User/workspaceStorage/ |
500 MB to 2 GB | Per-workspace .pack files and DBs |
State, cheap to rebuild |
~/.codex/sessions/ |
200 MB to 1 GB | Codex CLI rollout sessions | State, cheap to reset |
~/.ollama/models/ |
0 unless you pull a new model | GGUF model blobs | Yes, ollama pull refetches |
~/.cache/huggingface/hub/ |
0 to model-blob size | HF Hub model snapshots | Yes, refetches on first use |
~/Library/Caches/pypoetry/, ~/Library/Caches/pip/ |
50 MB to 300 MB | Python wheel + build cache | Yes, pip install rebuilds |
Any node_modules/ |
Full re-write on every install |
Package tree | Yes, npm install from lockfile |
Every row is reproducible from source or a lockfile. None of it belongs in a backup. By default, Time Machine backs up all of it, every hour, every day. Multiply daily churn by 365 across a year of snapshots and a 512 GB source disk lands on a 4 TB backup drive.
How do I audit what Time Machine is actually backing up?
Before touching Time Machine's exclusion list, run a read-only pass so you know what you are excluding and why. Nothing here writes anywhere.
# Baseline: how big is each Time Machine snapshot?
tmutil listbackups -m
# See the size delta between the newest snapshot and the current disk.
# This is the pile of data the next backup would copy.
tmutil compare -s
# Watch which paths Time Machine touches during a live backup.
# Ctrl-C when you have seen enough. Read-only, safe.
sudo fs_usage -w -f filesys backupd | grep -E 'DerivedData|Docker|claude|codex|Cursor|ollama|node_modules'
# Local (on-disk) snapshots eat SSD space too. List and inspect.
tmutil listlocalsnapshots /
diskutil apfs listSnapshots /
# Confirm the sizes of the dev-cache heavy hitters.
du -sh \
~/Library/Developer/Xcode/DerivedData \
~/Library/Containers/com.docker.docker/Data/vms/0/data/Docker.raw \
~/.claude/debug \
~/.claude/projects \
~/Library/Caches/claude-cli-nodejs \
~/Library/Application\ Support/Cursor/User/workspaceStorage \
~/.codex/sessions \
~/.ollama/models \
~/.cache/huggingface \
2>/dev/null
tmutil compare -s is the one that opened my eyes. It printed a 71 GB delta between two consecutive snapshots. On a Mac where I had done nothing all afternoon except run Claude Code and one Xcode build.
What did devs actually find when they went looking?
The exact same shape shows up in other people's audits. From the writeup Using Claude to free 200 GB from a full disk, the top-of-list culprit was container churn, not a growing source tree:
"Docker was the biggest culprit... The volumes were almost entirely unused, probably from old postgres/redis containers I'd stopped months ago."
Every one of those old volumes lives inside Docker.raw. Every change to Docker.raw looks like a full-file change to Time Machine, because it is a sparse container. Ninety GB of "unused" Docker data on the source disk turns into a ninety GB churn line in every daily snapshot until you notice.
The Claude Code 472 GB thread on GitHub is the more dramatic version of the same story. The affected user did not lose disk to a source tree, they lost it to ~/.claude/debug plus MCP logs under ~/Library/Caches/claude-cli-nodejs/. That folder pattern is exactly the kind of thing Time Machine copies without knowing it is disposable.
Which folders should I actually exclude from Time Machine?
Once the audit is done, the exclusion list writes itself. Everything below is reproducible from source, a lockfile, or a re-fetch. Add these in System Settings > General > Time Machine > Options, or with tmutil addexclusion -p.
# Xcode caches. Rebuilt by xcodebuild on next open.
tmutil addexclusion -p ~/Library/Developer/Xcode/DerivedData
tmutil addexclusion -p ~/Library/Developer/CoreSimulator/Caches
tmutil addexclusion -p ~/Library/Caches/com.apple.dt.Xcode
# Docker. Rebuilt by docker pull.
tmutil addexclusion -p ~/Library/Containers/com.docker.docker/Data/vms/0/data
tmutil addexclusion -p ~/Library/Containers/com.docker.docker/Data/vms/0/log
tmutil addexclusion -p ~/Library/Group\ Containers/group.com.docker
# AI tool caches and diagnostics. Regenerated on next run.
tmutil addexclusion -p ~/.claude/debug
tmutil addexclusion -p ~/.claude/projects
tmutil addexclusion -p ~/Library/Caches/claude-cli-nodejs
tmutil addexclusion -p ~/.codex/sessions
tmutil addexclusion -p ~/.codex/log
# Editor state. Rebuilt by the editor.
tmutil addexclusion -p ~/Library/Application\ Support/Cursor/User/workspaceStorage
tmutil addexclusion -p ~/Library/Application\ Support/Windsurf/User/workspaceStorage
# Package manager caches. Refetched from lockfile.
tmutil addexclusion -p ~/.npm
tmutil addexclusion -p ~/Library/Caches/Yarn
tmutil addexclusion -p ~/Library/pnpm
tmutil addexclusion -p ~/.bun/install/cache
# Local LLM models. Refetched by ollama pull or hf.
tmutil addexclusion -p ~/.ollama/models
tmutil addexclusion -p ~/.cache/huggingface
# Playwright browsers. Refetched by playwright install.
tmutil addexclusion -p ~/Library/Caches/ms-playwright
# Verify what is now excluded.
mdfind "com_apple_backup_excludeItem = 'com.apple.backupd'"
The node_modules folders scattered under ~/Developer/ or ~/Code/ are the last piece. You can add each one with tmutil addexclusion -p, but the more robust move is to keep source trees under a path that is already excluded, and let npm install rebuild the tree in place. For an audit of just how many node_modules folders one Mac tends to carry, find-all-node-modules-mac walks through the read-only pass I use.
How did the 4 TB backup actually shrink?
Two things happened in order.
First, a Trash-first sweep of the dev-cache heavy hitters themselves. Not to shrink the backup drive, but to stop the churn at the source. Move ~/.claude/debug to Trash, quit and reopen Xcode after moving DerivedData, run docker system prune -a --volumes. Daily delta drops from tens of GB to hundreds of MB overnight.
Second, add the exclusion list above. New snapshots stop copying the excluded folders at all. Time Machine's own reap loop, which deletes old snapshots under space pressure, then chews through the old copies of DerivedData and Docker.raw within a few weekly cycles. Within about six weeks, the used space on the 4 TB drive dropped below 900 GB and stayed there. There is no way to instantly shrink an existing Time Machine backup without deleting individual snapshots by hand, but the reap loop does the right thing on its own once the churn is capped.
If you would rather see a founder-level version of the same audit for a MacBook, system-data-180gb-autopsy walks through the same folders on the source disk instead of the backup drive.
Does CleanMyDev do this for you?
Not the Time Machine part. CleanMyDev never edits Time Machine settings, and I have no plans to change that. Time Machine's exclusion list is a piece of macOS state I want you to keep control of.
What CleanMyDev does do is the receipts pass over the source folders. Every path in the churn table above is a first-class row inside CleanMyDev, with size, last-modified, owning tool, and a risk label. Move to Trash is the default. The seven-day Trash window means every sweep is reversible. Once you have used CleanMyDev to pare the source folders down, adding the exclusion list to Time Machine on top is a five-minute follow-up that keeps them from coming back.
CleanMyDev is $9.99 lifetime. No subscription, no telemetry, no scareware. If the audit above resonates, pick it up on the pricing page and run the receipts pass before you edit a single Time Machine setting.
Related reading
- i-freed-147gb-macbook-cleanmydev, the same audit on the source disk, timestamped end to end.
- claude-code-472gb-postmortem, postmortem of the folder Time Machine loved to copy every hour.
- ai-tools-disk-footprint-16-months, sixteen months of AI tool churn, per tool, with receipts.