On this page
Every few months, my Mac runs out of space — and the culprit is rarely photos or videos. It’s 19 GB of Gradle caches, 8 GB of Xcode DerivedData, and node_modules directories from projects I haven’t opened in a year.
I work across Flutter, Node, Python, iOS, and Docker, so every stack leaves behind its own build artifacts and package caches. macOS reports them as storage usage, but it can’t tell me which ones are safe to remove.
The last time this happened, I documented the cleanup and turned what I learned into reclaim, an open-source CLI that finds developer-generated disk usage and helps remove it safely. My last full cleanup with it freed 98 GB. This post covers the manual route first — what to inspect, what to delete, what to leave alone — and then the tool.
First, see where the space is going
Before deleting anything, measure. These commands are read-only:
du -sh \
~/Library/Developer/Xcode/DerivedData \
~/.gradle/caches \
~/.npm \
2>/dev/null | sort -h
docker system df -v
brew cleanup --dry-run
Most of what you’ll find is regenerable — but “regenerable” doesn’t always mean “zero consequence.” The way to stay safe is to sort everything into three buckets: rebuilds itself, review first, and never touch.
Safe to rebuild: the big caches
Xcode DerivedData — intermediate build products and indexes. Often the single biggest cache on an iOS/macOS developer’s machine. Deleting it means the next build and indexing pass take longer; nothing is lost:
rm -rf ~/Library/Developer/Xcode/DerivedData
Gradle — Gradle does clean unused cache entries periodically, but on a machine that builds several Android or JVM projects the cache still grows into the tens of gigabytes. Mine was 19 GB. The next build re-downloads what it needs:
rm -rf ~/.gradle/caches
JavaScript package caches — these are the global download caches, separate from any project’s node_modules. npm’s cache is self-healing: npm cache verify checks integrity and garbage-collects without dropping everything, while clean --force is the reclaim-disk-space option:
npm cache verify # integrity check + garbage collection
npm cache clean --force # or: empty the cache entirely
pnpm store prune
yarn cache clean
Homebrew — brew cleanup removes old versions and stale downloads. Homebrew runs this automatically every 30 days, so the gains are usually modest — the --dry-run from earlier shows exactly what it would remove.
Review first: Docker and stale projects
Docker deserves inspection before deletion. Start narrow:
docker system df -v # what's actually using space
docker builder prune # build cache only
docker system prune is the broader sweep: it also removes all stopped containers and unused networks. A stopped container can hold data in its writable layer that does not regenerate — check docker ps -a before running it. One macOS-specific nuance: Docker Desktop stores all Linux-side data in a single large disk image, so unused images and build cache consume real host space, and deleting files inside a running container doesn’t necessarily return that space to macOS.
Stale node_modules — the sneaky one. Each folder is only a few hundred megabytes, but across every project you’ve ever cloned it often outweighs any single cache. It’s reproducible as long as the lockfile is valid and the packages are still available from the registry — true for almost anything recent, less certain for a six-year-old project. The judgment call is which projects: in an active repo you’re just paying for a reinstall, so target the ones you’ve genuinely abandoned. The same logic applies to Rust target/ directories and Python virtualenvs.
What should never be on the list
Source code. Anything under version control — especially with uncommitted or unpushed work. Dotfiles, credentials, and profile data. Databases belonging to local services. No cleanup pass should go near these, no matter how large they’ve grown; if a “cache” folder might contain any of them, that’s a review-first item at best.
Where manual cleanup stops scaling
The manual route works. The problem is that it’s a scavenger hunt across a dozen tools, the safety judgment is different for each one, and the list keeps growing — Cargo, CocoaPods, Playwright’s browser binaries, Hugging Face model caches, simulator runtimes. I did the hunt one time too many.
What reclaim does differently
reclaim is a single-binary CLI (Go, MIT-licensed) that scans everything at once and presents an interactive picker:
🧹 reclaim — interactive cleanup
Selected: 21.4 GB of 47.2 GB
▼ Build caches — 25 GB [3/3 items, 25 GB selected]
[✓] ✓ ~/.gradle/caches 19.2 GB
[✓] ✓ ~/.gradle/wrapper 4.2 GB
▼ Package manager caches — 7.7 GB [4/8 items, 4 GB selected]
[✓] ✓ ~/.npm/_cacache 3.3 GB
[ ] ✓ ~/.pub-cache 3.1 GB
▼ IDE & editor caches — 8.3 GB [1/3 items, 8.0 GB selected]
[✓] ✓ ~/Library/Developer/Xcode/DerivedData 8.0 GB
The catalog covers 70+ rules across build caches (Gradle, Maven, Go, Cargo, Bazel), package managers (npm, pnpm, Bun, pip, uv, poetry, conda, CocoaPods, Homebrew), IDE artifacts (Xcode, VS Code, Cursor, JetBrains), containers (Docker, Colima), testing tools (Playwright, Cypress), and AI/ML caches (Hugging Face, Ollama, PyTorch).
Two things do the work the manual route can’t:
- Git-based staleness detection. For project artifacts like
node_modulesandtarget/, reclaim reads each project’s git history to see when you last touched it. Abandoned projects get flagged; the repo you deployed yesterday doesn’t. - Tool and app awareness. Vendor cleanups like
docker system pruneare only offered when the tool is actually available to run them, and rules can require apps like Docker Desktop or Xcode to be closed before their managed files are touched.
reclaim isn’t the only cleaner that recognizes developer caches — commercial tools have started covering npm, Gradle, and Docker too. Its difference is transparency and project awareness: it’s free and open source, uses git history to identify stale artifacts, assigns every rule a visible safety tier, records every deletion, and lets you add rules without touching the Go code. No subscription, no telemetry, one auditable binary.
The safety model
A cleanup tool’s failure mode is catastrophic, so safety is the design center:
- Every rule carries a visible tier. ✓ safe regenerates with zero loss; ⚠ confirm should be reviewed first; ✗ dangerous is never auto-deleted — the picker won’t even let you select it.
- Deletion is always an explicit choice. Interactive cleanup asks before deleting, and “Quick clean” touches safe items only. In automation, passing
--applyis the explicit opt-in (it covers safe and confirm tiers); without it,--plainonly reports what it finds. - A last line of defense in code. For filesystem deletions, an independent path guard rejects system paths, anything outside your home directory, and the home directory itself — regardless of what the rule catalog says. The handful of rules that delegate to vendor commands (
brew cleanup,docker system prune) do what the vendor tool does, which is exactly why they sit behind the review tiers above. - An audit trail. Every apply run appends a JSONL record to
~/.reclaim/logs/— rule, path, bytes freed, errors — so you can always answer “what did it delete last Tuesday?” - No telemetry or usage reporting. Cleanup runs entirely locally. Interactive modes can make one optional daily request to GitHub to check for updates; disable it with
--no-update-checkorRECLAIM_NO_UPDATE_CHECK=1.--plainand--applynever check.
Install reclaim and run a dry scan
Homebrew on macOS (via the ImadRashid/tap):
brew install ImadRashid/tap/reclaim
reclaim --plain
--plain deletes nothing — it prints a report of how much developer-generated storage your Mac has accumulated, which is the right first run. Prebuilt binaries for macOS (Apple Silicon and Intel) and Linux are on the releases page; it’s a single static file with no runtime.
FAQ
Does it work on Linux or Windows? Linux binaries ship with every release and most rules apply there. Windows isn’t supported.
Can I add my own rules? Yes — a rule is a YAML entry (paths, safety tier, one-line description), no Go code involved.
What’s planned next? Planned improvements are tracked in the GitHub repository.
If your Mac is perpetually 10 GB from full, a dry scan will probably surprise you. And if reclaim helps, a star on GitHub makes it easier for other developers to discover. It’s the second small tool I’ve open-sourced recently — the first, csm, manages multiple Claude Code accounts the way nvm manages Node versions.