nvm (Node Version Manager) installs Node.js versions into your home directory and switches between them with one command. It’s the standard tool when different projects need different Node versions, or when you want to test an upgrade without touching the system.
Install nvm
curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.40.3/install.sh | bash
Replace v0.40.3 with the latest release from the nvm-sh/nvm GitHub repository. The script clones nvm into ~/.nvm and adds these lines to ~/.bashrc (or ~/.zshrc):
export NVM_DIR="$HOME/.nvm"
[ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh"
[ -s "$NVM_DIR/bash_completion" ] && \. "$NVM_DIR/bash_completion"
Load it in the current shell and check:
source ~/.bashrc
command -v nvm # prints "nvm" — it's a shell function, not a binary
which nvm prints nothing, and that’s normal.
Install Node versions
nvm install --lts # latest LTS release
nvm install 22 # latest 22.x
nvm install 20.18.1 # an exact version
nvm ls # installed versions
nvm ls-remote --lts # LTS versions available to install
The first version you install becomes the default.
Switch versions
nvm use 20
node -v
nvm use --lts
nvm use changes the version for the current shell only. To change what new shells get:
nvm alias default 22
Pin a version per project with .nvmrc
In the project root:
echo "22" > .nvmrc
Then anyone on the project runs:
nvm install # installs the version in .nvmrc if missing
nvm use # switches to it
Commit .nvmrc to the repository. Many CI systems and hosting platforms read it too.
Global packages are per version
Each Node version has its own global node_modules. After installing a new version, bring your global tools along:
nvm install 24 --reinstall-packages-from=22
Update and remove
nvm install --lts --latest-npm # newest LTS with the newest npm
nvm uninstall 18
To update nvm itself, re-run the install script with the new version number.
nvm on servers: cron, systemd and PM2
nvm is designed for interactive shells. Non-interactive contexts don’t load ~/.bashrc, so node isn’t found. Solutions:
Use the absolute path. Find it with:
nvm which 22
# /home/deploy/.nvm/versions/node/v22.11.0/bin/node
A systemd service then uses ExecStart=/home/deploy/.nvm/versions/node/v22.11.0/bin/node server.js. The downside: the path changes when you upgrade.
Cron jobs: source nvm explicitly:
*/10 * * * * bash -lc 'source ~/.nvm/nvm.sh && cd ~/app && nvm exec 22 node task.js'
PM2: start PM2 from a shell where the right version is active; it records the interpreter path. After switching Node versions, update PM2 and its startup script:
npm install -g pm2
pm2 update
pm2 startup # re-run the printed command
For servers that run one Node version for everything, a system-wide install from NodeSource avoids these issues — see installing Node.js on Ubuntu.
Alternatives
- fnm — a fast nvm-compatible manager written in Rust; reads
.nvmrc. - Volta — pins Node and package-manager versions in
package.json. - mise (formerly rtx) — one tool for Node, Python, Go, Ruby and more.
Troubleshooting
nvm: command not found— the init lines aren’t in your shell’s rc file, or you haven’t opened a new shell. Check~/.bashrcvs~/.zshrc.- Slow shell startup — nvm adds noticeable startup time. Add
--no-useto the source line and callnvm usewhen needed, or switch to fnm. npmpermission errors — never usesudowith an nvm-installed Node; everything lives in your home directory.
Next, keep your app running with the PM2 process manager. Any Linux VPS with SSH access is ready for nvm in under a minute.