You Have 40 GB of node_modules You Forgot About
Not the project you are working on — the sixty others. How to find every node_modules folder on your Mac, rank them by how long since you touched them, and delete the dead ones safely.
The node_modules in the project you have open right now is fine. Leave it alone.
The problem is the other sixty. The tutorial you followed in 2024. The take-home assignment. The three abandoned side projects. The repo you cloned to read one file. Each one carries 200 MB to 1.5 GB of dependencies, and every one of them is perfectly reproducible from a lockfile.
Find every one of them
find ~ -type d -name node_modules -prune -print0 2>/dev/null \
| xargs -0 -n1 du -sh 2>/dev/null | sort -hr | head -40
Two details in there matter:
-prunestopsfinddescending into anode_modulesonce it has matched it. Without it you get every nested one too, sizes double-count, and the totals are meaningless.-print0/-xargs -0survives paths with spaces, which you will have.
Give it a minute on a large home directory. The output is one line per project, biggest first, and it is usually a longer list than people expect.
Rank by last touched, not by size
Size tells you what is expensive. Age tells you what is dead. The second question is the one you actually want answered:
find ~ -type d -name node_modules -prune 2>/dev/null \
| while read -r d; do
printf '%s\t%s\n' "$(stat -f '%Sm' -t '%Y-%m-%d' "$d")" "$d"
done | sort | head -40
That prints oldest first, YYYY-MM-DD then path. Anything last modified more than a year ago is a
free deletion — you are not going back to that project this week, and if you do, npm install
restores it exactly.
Delete them
Once you have read the list and recognised the projects:
rm -rf ~/projects/old-thing/node_modules
To clear everything untouched in the last year in one pass, look first:
find ~ -type d -name node_modules -prune -mtime +365 -print 2>/dev/null
Read that output. Actually read it. Then, and only then:
find ~ -type d -name node_modules -prune -mtime +365 -print0 2>/dev/null \
| xargs -0 rm -rf
If you would rather do it interactively, npkill is a
well-known npx tool that lists every node_modules in a terminal UI and deletes on keypress:
npx npkill
What you can and cannot restore
You can always restore node_modules as long as the project has a lockfile — package-lock.json,
pnpm-lock.yaml or yarn.lock — and the packages are still published. npm ci reproduces the exact
tree in seconds.
The three cases where you should stop and think:
- No lockfile committed. Restoring gives you today’s versions matching your semver ranges, not the versions that were there. For a project that still needs to build reproducibly, commit the lockfile before you delete.
- You patched something inside
node_modulesby hand. It is gone. If you usepatch-packagethe patches live inpatches/and survive; ad-hoc edits do not. - A dependency has been unpublished or the registry is private and now unreachable. Rare, but
npm ciwill simply fail. If the project matters and it depends on something fragile, keep it.
The global caches are a separate 5–15 GB
Deleting node_modules folders does not touch your package manager’s global store, which is its own
pile:
# npm
du -sh ~/.npm/_cacache
npm cache clean --force
# pnpm — ask it where the store is, the default moves
du -sh "$(pnpm store path)"
pnpm store prune
# Yarn
yarn cache clean
pnpm store prune is the polite one: it removes only packages no longer referenced by any project
on your machine. npm cache clean --force empties everything; npm re-downloads on demand, so the
only cost is bandwidth.
Why pnpm makes this problem much smaller
If you routinely have a dozen active JavaScript projects, this is the structural fix.
pnpm keeps one content-addressed store and hard-links into each project instead
of copying. Ten projects sharing React store one copy of React, not ten. Real-world disk use for
node_modules typically drops by well over half, and installs get faster as a side effect.
It will not retroactively clean up the npm-installed folders you already have — delete those with the commands above first, then reinstall with pnpm.
This is a recurring tax, not a one-off
You will clone something next week. It will pull 400 MB. In six months you will have forgotten it exists, and the only trace will be a disk-full warning with no explanation attached.
DevCruft finds every node_modules on every volume, shows each one sized to scale against the
rest of your disk with the date you last opened the project, and clears the dead ones in a batch.
The commands above do the same job for free — this is a real problem either way, and it is worth
twenty minutes of your attention today.
Next: the full developer storage guide, or what Docker is hiding from you.