Most production servers run one version of Node.js. But if you maintain multiple projects, or you are testing a migration from Node.js 22 to 24, you need both versions on the same machine without them stepping on each other. NVM (Node Version Manager) handles this. It installs each version in an isolated directory under ~/.nvm/versions/, keeps global npm packages separate per version, and lets you switch with a single command.
This guide covers NVM from install through real-world workflows: switching between versions, pinning a version per project with .nvmrc, understanding how global packages stay isolated, running one-off commands against a different version, migrating packages when upgrading, and cleaning up versions you no longer need. Everything was tested on an Ubuntu 24.04 VM with Node.js 24.14.0 (Krypton), 22.22.1 (Jod), and 20.20.1 (Iron) installed side by side.
Verified working: March 2026 on Ubuntu 24.04 LTS, Debian 13, Rocky Linux 10. NVM v0.40.4, Node.js 24.14.0 / 22.22.1 / 20.20.1
Install NVM
NVM works on any Linux distribution, macOS, and WSL. It installs to your home directory and requires no root access. The install script auto-detects your shell (bash, zsh, ksh) and adds the loader to the right profile file.
Grab the latest version from GitHub:
NVM_VER=$(curl -s https://api.github.com/repos/nvm-sh/nvm/releases/latest | grep tag_name | cut -d \" -f4)
curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/${NVM_VER}/install.sh | bash
Reload your shell to pick up the NVM function:
source ~/.bashrc
For zsh users, reload ~/.zshrc instead. Confirm NVM is loaded:
nvm --version
0.40.4
NVM is a shell function, not a binary. If which nvm returns nothing, that’s normal. Use command -v nvm to check if it’s loaded (should print nvm).
Install Multiple Node.js Versions
Install the versions you need. NVM downloads prebuilt binaries, so each install takes a few seconds:
nvm install 24
nvm install 22
nvm install 20
NVM resolves the major version to the latest patch. On our test VM, this installed:
v24.14.0 (npm v11.9.0) - Active LTS "Krypton"
v22.22.1 (npm v10.9.4) - Maintenance LTS "Jod"
v20.20.1 (npm v10.8.2) - End of Life "Iron"
You can also install by LTS codename or the literal word node for the latest Current release:
nvm install --lts
nvm install lts/jod
nvm install node
List everything installed:
nvm ls
Output from our VM:
v20.20.1
v22.22.1
-> v24.14.0
default -> 24 (-> v24.14.0)
lts/iron -> v20.20.1
lts/jod -> v22.22.1
lts/krypton -> v24.14.0
The arrow (->) marks the active version. Each version takes about 200 MB of disk space.
Switch Between Versions
Switching is instant because NVM just changes the PATH:
nvm use 22
node -v
Now using node v22.22.1 (npm v10.9.4)
v22.22.1
Switch back:
nvm use 24
node -v
Now using node v24.14.0 (npm v11.9.0)
v24.14.0
Set the default version for new terminal sessions:
nvm alias default 24
Without this, every new shell starts with whatever the system default was before NVM loaded.
Pin a Version Per Project with .nvmrc
This is where NVM becomes essential for teams. Drop a .nvmrc file in your project root, and anyone on the team (or CI) can run nvm use to get the right version without guessing.
Create the file with the desired version:
cd ~/my-project
echo "22" > .nvmrc
Then activate it:
nvm use
NVM reads the .nvmrc and switches automatically:
Found '/home/ubuntu/my-project/.nvmrc' with version <22>
Now using node v22.22.1 (npm v10.9.4)
The file accepts major versions (22), full versions (22.22.1), or LTS codenames (lts/jod). Major versions are best for most projects because they track the latest patch automatically.
Commit the .nvmrc to git. New team members just clone and run nvm use.
Global Packages Stay Isolated
Each Node.js version gets its own node_modules for globally installed packages. A package installed on Node.js 24 is not visible when you switch to 22. We tested this:
nvm use 24
npm install -g typescript
tsc --version
Version 6.0.2
Switch to 22 and check:
nvm use 22
which tsc
Nothing. TypeScript is only on 24. This isolation is intentional because different Node.js versions may need different versions of the same tool. But it means you need to reinstall global packages after adding a new Node.js version.
Migrate Global Packages When Upgrading
When installing a new version, use --reinstall-packages-from to copy your global packages over:
nvm install 24 --reinstall-packages-from=22
This installs Node.js 24 (or confirms it’s already installed) and then runs npm install -g for every package that was globally installed on Node.js 22. Saves the manual work of reinstalling each one.
Run Commands Against a Specific Version
Sometimes you need to run a quick check against a version without actually switching. nvm run and nvm exec handle this:
nvm run 22 -e "console.log('Running on', process.version)"
Running node v22.22.1 (npm v10.9.4)
Running on v22.22.1
nvm exec runs any command (not just node) with a specific version’s PATH:
nvm exec 22 npm -v
nvm exec 24 npm -v
10.9.4
11.9.0
This is useful in CI scripts where you want to test against multiple versions without switching the active shell.
Find Where Versions Are Stored
NVM stores everything under ~/.nvm/. Each version lives in its own directory:
nvm which 22
nvm which 24
/home/ubuntu/.nvm/versions/node/v22.22.1/bin/node
/home/ubuntu/.nvm/versions/node/v24.14.0/bin/node
Each version takes about 200 MB. With three versions installed, our test VM used 515 MB total for the entire ~/.nvm directory. Clean up versions you no longer need to free space (see below).
Remove Versions
Uninstall a specific version:
nvm uninstall 20
Uninstalled node v20.20.1
You cannot uninstall the currently active version. Switch to a different one first with nvm use.
Uninstall NVM Completely
Remove the NVM directory:
rm -rf ~/.nvm
Then edit ~/.bashrc (or ~/.zshrc) and remove the three NVM lines that the installer added:
export NVM_DIR="$HOME/.nvm"
[ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh"
[ -s "$NVM_DIR/bash_completion" ] && \. "$NVM_DIR/bash_completion"
Reload the shell and NVM is gone.
NVM vs NodeSource vs Tarball
NVM is not the only way to install Node.js. Here is when to use each approach:
| Method | Best for | Automatic updates | Multiple versions | sudo required |
|---|---|---|---|---|
| NVM | Development, CI, testing | No (manual nvm install) |
Yes | No |
| NodeSource apt/dnf | Production servers | Yes (via apt/dnf) | No (one version) | Yes |
| Binary tarball | Air-gapped systems, containers | No | No | Yes |
One important limitation: NVM installs Node.js in your home directory, so systemd services and other system users cannot access it. If you need Node.js for a production daemon, use NodeSource on Ubuntu or NodeSource on Rocky Linux instead.
Quick Reference
| Command | What it does |
|---|---|
nvm install 24 |
Install Node.js 24 (latest patch) |
nvm install --lts |
Install the latest LTS release |
nvm use 22 |
Switch to Node.js 22 in the current shell |
nvm use |
Read version from .nvmrc in current directory |
nvm alias default 24 |
Set default for new shells |
nvm ls |
List installed versions |
nvm ls-remote --lts |
List available LTS versions |
nvm run 22 script.js |
Run a script with Node.js 22 without switching |
nvm exec 22 npm -v |
Run any command with Node.js 22’s PATH |
nvm which 24 |
Show the binary path for Node.js 24 |
nvm current |
Show the active version |
nvm uninstall 20 |
Remove Node.js 20 |
nvm install 24 --reinstall-packages-from=22 |
Install 24 and copy global packages from 22 |
Full documentation is on the NVM GitHub repository. For installing a specific Node.js version without NVM, see our guides on Node.js 24 on Debian and Node.js on Ubuntu/Debian.