ShodhanaGuides

node_modules is eating my disk — what to do

Last updated: August 2026

Safe to delete — conditionally

A node_modules folder is reproducible from package.json plus a lockfile. If both are present and committed, deleting it costs you one install.

The condition is doing real work: reinstalling needs the network, the registry, and a toolchain that still builds the same native modules. Four cases break that — they are listed below, and every one of them shows up on old projects, which is exactly what you are about to delete.

Count them without double-counting

The trap in every version of this command you will find elsewhere is nested node_modules. Without -prune, find descends into dependencies' own dependency folders and reports each one again inside its parent's total.

find ~ -name node_modules -type d -prune -print0 2>/dev/null \
  | xargs -0 du -sh | sort -h

-prune tells find to stop descending once it has matched, so each top-level folder is counted exactly once.

Sort by what you can actually afford to lose

Size alone is the wrong signal — the biggest folder is usually your current project. What you want is size next to last activity, so abandoned projects sort themselves to the top of the delete list:

find ~ -name node_modules -type d -prune 2>/dev/null | while read -r d; do
  p=$(dirname "$d")
  printf '%s\t%s\t%s\n' \
    "$(du -sh "$d" | cut -f1)" \
    "$(git -C "$p" log -1 --format=%as 2>/dev/null || echo no-git)" \
    "$p"
done | sort -h
1.2G	2026-07-30	/Users/you/work/current-app1.9G	2024-02-11	/Users/you/side/old-dashboard2.4G	no-git	/Users/you/Downloads/tutorial-clone

That last column is the decision. A 2.4 GB folder from a tutorial you followed once is free space. A 1.2 GB folder from last week is your job.

Shodhana's Build Artifacts category listing per-project build output — a Rust project, a Gradle project and a Python project — each with its toolchain, path, size and last-used date.
The same size-plus-last-activity pairing the command above produces, resolved per project. Each row is matched against the manifest that proves what the project is, which is what stops a folder that merely shares a name from being counted.

The four cases where reinstall does not work

Each of these is more likely the older the project, which makes them correlate precisely with the folders you are most tempted to delete.

The cheap insurance: before deleting an old project's folder, confirm the lockfile is committed.

git -C ~/side/old-dashboard status --short -- '*lock*'

Silence means it is tracked and clean. Anything printed means the lockfile is untracked or modified, and deleting node_modules throws away the only record of what actually worked.

Delete safely

Send them to the Trash rather than rm -rf, so a mistake is recoverable for as long as you leave the Trash alone:

find ~/side ~/Downloads -name node_modules -type d -prune 2>/dev/null \
  | xargs -I{} mv {} ~/.Trash/

Note the explicit directories. Running this rooted at ~ will also take the project you have open right now.

Stop it recurring: use a shared store

npm and Yarn Classic copy every dependency into every project, so twelve projects sharing one 400 MB dependency tree cost you 4.8 GB. pnpm keeps one content-addressed copy in a global store and hard-links it into each project. The twelve projects then cost roughly 400 MB in total.

pnpm store pathpnpm store prune

store prune drops packages no project references any more. It is safe — anything still linked is retained by definition.

Monorepos count differently

In a workspace-based repo you have a root node_modules plus one inside each package. -prune only stops descending inside a folder it has matched, so packages/api/node_modules is still found and reported — it is not nested within the root node_modules, just alongside it.

That is the behaviour you want. It also means a single monorepo can produce a dozen rows in the listing above, and the total for the repo is the sum of them rather than any one line. If you are deciding whether a monorepo is worth deleting, total it explicitly:

find ~/work/monorepo -name node_modules -type d -prune -print0 2>/dev/null \
  | du -ch --files0-from=- 2>/dev/null | tail -1

On a Mac without GNU coreutils, du has no --files0-from; brew install coreutils and use gdu, or simply add up the rows from the earlier command.

The package cache is a separate thing

Deleting node_modules does not touch your package manager's download cache, which is its own multi-gigabyte item:

ManagerCache locationClear with
npm~/.npm/_cacachenpm cache clean --force
pnpmpnpm store pathpnpm store prune
Yarnyarn cache diryarn cache clean

Clearing these costs only re-download time. Full sizes and commands for every other toolchain are on the cache sizes page.

Read next

Shodhana does this check for you: every node_modules matched back to its project's manifest and lockfile, sized, dated by last use, and never confused with a folder that merely shares the name. See the developer view.