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 ~/.bashrc vs ~/.zshrc.
  • Slow shell startup — nvm adds noticeable startup time. Add --no-use to the source line and call nvm use when needed, or switch to fnm.
  • npm permission errors — never use sudo with 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.

  • Node.js
  • nvm
  • Linux