Someone at your team switched from npm to pnpm because "it saves disk space", and then a month later Finder tells you ~/Library/pnpm is 12 GB and growing. Both statements can be true at once. pnpm really does save space compared to npm — but it saves it by consolidating what would otherwise be duplicated inside every node_modules, not by making the underlying packages smaller.
If you understand the store, you can prune it confidently. If you do not, you will either delete it and lose the install-time speedup, or you will leave it alone forever and watch it grow.
Where the pnpm store lives on macOS
pnpm keeps a single content-addressable store per Mac. On recent versions the default is ~/Library/pnpm/store (Homebrew and Corepack installs) or ~/.local/share/pnpm/store if you installed via the standalone script. You can confirm where yours actually is:
pnpm store path
Inside the store you will see subdirectories named v3, v10, and so on — one per store schema version. Each of those holds the actual package files, addressed by hash. The store is deliberately shared across every project on the machine.
How hard-linking makes node_modules cheap
When you run pnpm install inside a project, pnpm does not copy files into node_modules. It creates hard links from node_modules/.pnpm/<pkg>@<version>/node_modules/<pkg>/* back into the store. A hard link is a second name for the same on-disk data. From macOS's point of view, the file exists in both places; from the filesystem's point of view, it is one block of bytes with two directory entries.
That means installing React 18 into ten projects does not copy React ten times. It copies it once, into the store, and then adds ten sets of hard links. Total on-disk cost: roughly the size of one copy.
There are two consequences worth knowing:
du -sh node_moduleswill report the full nominal size, becauseducannot tell that the bytes are shared with the store. The number is misleading in the direction of overstating usage.- Deleting
node_modulesfrom a project does not free the shared bytes. Those live in the store until nothing references them.
Why the store looks large
Two versions of the same package are stored separately in the CAS. React 17, React 18, and React 19 are three distinct entries. Multiply that across every dependency your projects have used over two or three years and the store fills up honestly. Nothing is duplicated within a single version, but every distinct version has to be there for the projects that pin to it.
A store measuring 8 to 15 GB after a couple of years of active JavaScript work is normal. It is not a leak.
What pnpm store prune actually does
pnpm store prune walks the store and removes any package version that no project on the machine currently references. It figures out what is referenced by scanning the pnpm lockfiles it knows about and by looking at the reflink map inside the store.
pnpm store prune
Two things follow from how this works:
- It is safe. It will not remove a package that is still linked from some
node_moduleson disk. - It only helps if you have deleted or updated projects that pinned old versions. If every project on your Mac still references every version in the store, prune has nothing to remove.
If you have not touched some old projects in months but their node_modules is still on disk, prune will leave those versions alone. Delete node_modules from those stale projects first, then prune.
When to run prune
A reasonable cadence is quarterly, or after any of these events:
- You archived or deleted a batch of old repositories.
- You bumped a major framework version across every active project (all the old versions are now unreferenced).
- You noticed
pnpm store pathis over 20 GB and you want to see how much of it is actually needed.
pnpm store prune prints a summary of how many packages were removed and how much space it recovered. If the answer is under 100 MB, the store is not the thing filling your disk.
Disaster recovery: deleting the store entirely
If the store is genuinely corrupted — for example, you had a disk error and pnpm now reports mismatched integrity — you can delete the whole thing:
rm -rf "$(pnpm store path)"
Every existing node_modules on the machine will still work for now, because the hard links keep the underlying data alive. Once you next run pnpm install in a project, pnpm will detect that the store is missing and re-download from the registry to rebuild it. That first install will be slow. Subsequent installs will be fast again.
You do not need this often, and you should not do it as a "when in doubt, restart" reflex. Prefer pnpm store status and targeted repair first:
pnpm store status
pnpm store versus the global pnpm folder
~/Library/pnpm (or ~/.local/share/pnpm) contains more than just the store. It also holds:
- The store itself, under
store/. - Downloaded pnpm binaries when you use
pnpm self-update. - Global packages installed via
pnpm add -g, underglobal/.
If your disk usage on ~/Library/pnpm is large but pnpm store path reports a much smaller number, the extra size is in global packages or old pnpm binaries. pnpm list -g shows the global list; you can remove things you do not need with pnpm remove -g <pkg>.
node_modules is still the biggest number, project by project
Even with pnpm's sharing, a single project's node_modules directory can look enormous in Finder because macOS counts the hard-linked bytes at every location. On a Mac with 30 active JavaScript projects, the sum of node_modules sizes reported by du might exceed your drive size. That is a Finder-side reporting quirk, not real usage.
The honest question is: how much would you save by deleting node_modules from a project? On disk, roughly zero, because the underlying files stay in the store. What you save is the directory entries and the small per-file overhead. What you gain is that the next pnpm install will be a fresh graph that reflects the current lockfile, which is often what you actually want on a stale project.
A sensible pnpm hygiene routine
- Run
pnpm store statusquarterly to see whether anything looks wrong. - Run
pnpm store pruneafter archiving or upgrading batches of projects. - Delete
node_modulesfrom stale projects you have not touched in months, then prune. - Only delete the store outright if you have a specific integrity or disk-corruption reason.
Seeing what the store really costs
We built VolumeLens to answer disk-usage questions honestly on macOS. It counts hard links once — the pnpm store's whole design breaks tools that do not — and it recognises pnpm's store as a distinct category, so you can see how much of your disk is really the store versus a pile of node_modules that Finder is inflating. If you would rather see the shape of your JavaScript work at a glance, take a look.