Node version managers solve a real problem — different projects pinned to different Node versions, switchable in seconds — but they solve it by installing a complete, independent copy of Node (interpreter, npm, and every globally installed package) for every version you've ever used. On a machine that's followed a few years of Node's release cadence, that adds up to more installed Node versions than most people realise they still have.
Where nvm stores its versions
nvm (Node Version Manager) installs every version under ~/.nvm/versions/node/, one subfolder per version, named like v18.20.4 or v20.11.1. Each folder is a self-contained Node installation — its own bin, lib, and include directories, its own bundled npm, and critically, its own separate set of globally installed npm packages under that version's lib/node_modules.
nvm ls
lists every version currently installed, with the one currently active marked. Cross-reference against:
du -sh ~/.nvm/versions/node/*
to see the actual disk cost of each. Individual versions tend to run somewhere from 50 MB to a few hundred megabytes depending on how many global packages you've installed under each one — a version you used briefly for one project and installed a handful of CLI tools under can be surprisingly larger than a version you've only ever used for its interpreter.
Where fnm stores its versions
fnm (Fast Node Manager), a popular faster alternative to nvm written in Rust, uses a different default location: ~/.local/share/fnm/ on macOS (or ~/Library/Application Support/fnm depending on version and installation method — check with fnm env if you're unsure which your installation uses). The internal structure is conceptually the same as nvm's — one complete, independent Node installation per version.
fnm list
shows installed versions, and the standard du -sh approach works the same way once you know the base directory:
du -sh ~/.local/share/fnm/node-versions/*
(the exact subfolder naming can vary slightly by fnm version, so confirm the actual path on your machine with fnm env or by inspecting the directory directly before scripting anything against it).
Why old versions pile up
Both tools are designed around the assumption that switching Node versions should be instant and side-effect-free, which means neither one has a strong incentive to prompt you to remove old versions — they just sit there, fully functional, waiting to be reactivated if a project needs them again. Over a few years following Node's roughly-annual major release cadence, plus the occasional project pinned to an old LTS version for compatibility reasons, it's common to accumulate half a dozen or more installed versions, several of which haven't been the active version in a long time.
Removing versions you don't need
With nvm:
nvm uninstall 16.20.2
With fnm:
fnm uninstall 16.20.2
Both commands remove that version's complete installation directory, including any globally installed packages under it. Neither tool will let you uninstall the version currently active in your shell — switch to a different version first if needed.
Before uninstalling, it's worth checking whether any project on your machine still specifies that exact version via a .nvmrc or .node-version file, since removing a version a project depends on just means the next time you cd into that project and the manager tries to auto-switch, it will prompt to reinstall — not a disaster, but worth knowing rather than being surprised by.
grep -r "16" ~/Projects/*/.nvmrc 2>/dev/null
is a rough way to check across a projects folder, adjusting the path and version pattern for your setup.
The bigger cost hiding behind version choice: node_modules per project
It's worth being clear-eyed about scale here: the Node versions themselves, even accumulated across half a dozen releases, are rarely the dominant space consumer on a developer's Mac. Every individual project's node_modules folder — a full, independent copy of every dependency for that project, regardless of which Node version manages it — is typically far larger in aggregate, especially across many cloned repositories. Cleaning up old Node versions is worthwhile housekeeping, but it's not usually where the big win is.
find ~ -type d -name node_modules -prune -exec du -sh {} \; 2>/dev/null
walks your home directory for every node_modules folder and its size — a more productive place to look first if the goal is reclaiming meaningful space rather than tidying version sprawl.
Global packages multiply the cost of keeping old versions around
It's worth being specific about why a version you "just keep around in case" costs more than it looks. Every global npm package you install (npm install -g <tool>) while a given Node version is active gets installed under that version's own directory, not shared across versions. A developer who has, over the years, run npm install -g typescript, npm install -g eslint, or similar tools under several different Node versions in turn ends up with several independent copies of each tool, one per Node version, none of which the version manager will tell you about unless you check each version's global packages individually:
nvm use 18.20.4 && npm list -g --depth=0
repeated per version is the only reliable way to see what's actually accumulated. This is often a bigger contributor to a specific old version's disk footprint than the Node runtime itself.
Corepack and package manager version pinning
If you're using Corepack (bundled with recent Node releases) to manage per-project Yarn or pnpm versions, be aware that Corepack's own downloaded package manager binaries are cached separately again, typically under Corepack's own cache directory rather than inside the Node version folder itself. This is a smaller and generally well-behaved cache, but worth knowing about if you're doing a thorough audit of everything Node-tooling-adjacent on the machine, since it's yet another location that doesn't disappear when you uninstall an old Node version through nvm or fnm.
A practical routine
nvm lsorfnm listto see every installed version.du -sheach version's directory to find the actual disk cost per version.- Check any active projects'
.nvmrc/.node-versionfiles for versions you're about to remove. - Uninstall versions you're confident nothing depends on, keeping your current LTS and maybe one previous version for compatibility testing.
- Separately, and usually more productively, sweep for large
node_modulesfolders across old project clones you're not actively working on.
Where VolumeLens fits in
Node version sprawl and forgotten node_modules folders both live quietly under your home directory, invisible to the built-in Storage screen and easy to lose track of across years of JavaScript work. VolumeLens scans everything and shows both side by side, sized to scale, so you can decide in one glance whether it's the old Node versions or the node_modules trees actually worth cleaning up. Compare Free and Pro to see what Insights recognises automatically across your Node tooling.