Troubleshooting

nvm: command not found — load nvm in zsh, bash, cron and CI

nvm is a shell function, not a program: its loader lines must run in your shell. Add them to ~/.zshrc or ~/.bashrc, open a new terminal, run command -v nvm.

Published ~8 min read

Short answer: nvm is not a program on your PATH but a shell function, loaded into each new shell by lines the installer adds to a startup file. So nvm: command not found, or zsh: command not found: nvm on a Mac, means those lines did not run in this shell. Usually you are still in the terminal you installed from, they went to a file your shell does not read, or sudo, cron, a script or SSH runs the command without them. For your own terminal, put the lines into ~/.zshrc (zsh) or ~/.bashrc (bash):

~/.zshrc or ~/.bashrc
export NVM_DIR="$HOME/.nvm"
[ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh"  # This loads nvm
[ -s "$NVM_DIR/bash_completion" ] && \. "$NVM_DIR/bash_completion"  # This loads nvm bash_completion

Open a new terminal and run command -v nvm. It prints nvm when the function is loaded. If nothing changes, load nvm by hand in that terminal:

bash · zsh
. ~/.nvm/nvm.sh && nvm --version

A version number means nvm itself is fine and only the startup file is wrong (find it). “No such file or directory” (lowercase in zsh) means the install never finished. On a Mac, the installer then often stopped with You may be on a Mac, and need to install the Xcode Command Line Developer Tools. Run xcode-select --install and start the installer again.

Which message do you see?

MessageWhereCause and fix
zsh: command not found: nvmmacOS Terminal (zsh is the default), zsh on LinuxThe lines went to a bash file, or nowhere: which file
bash: nvm: command not foundLinux terminal right after the installSame terminal as the installer: open a new one
-bash: nvm: command not foundSSH login~/.bash_profile skips ~/.bashrc: SSH login
bash: line 1: nvm: command not found or -bash: line 1: nvm: command not foundssh HOST 'nvm …', deploy scripts, sudo -iu USERNon-interactive shell: fix
/bin/sh: 1: nvm: not found or /bin/sh: 1: source: not foundcron, Dockerfile RUN on Debian or Ubuntucron and systemd, Docker
status=203/EXECsystemd service with ExecStart=nvm or nodecron and systemd
sudo: nvm: command not foundsudo nvm …sudo
fish: Unknown command: nvmfishfish
'nvm' is not recognized as an internal or external command, or The term 'nvm' is not recognized as a name of a cmdletWindows cmd, PowerShellWindows

nvm is a function, not a file

The install script puts nvm into ~/.nvm and appends the lines above to a profile file. It runs as a separate bash process (that is what | bash means), and no child process can define a function in the shell that started it. That is why it ends with => Close and reopen your terminal to start using nvm or run the following to use it now:. Either open a new terminal or load the file by hand: source ~/.zshrc or source ~/.bashrc. In bash, a working nvm looks like this:

bash
command -v nvm      # prints: nvm
type nvm | head -1  # prints: nvm is a function

Do not test with which nvm. In bash it prints nothing even when nvm works, because which only looks for files on the PATH.

Find the file the installer wrote to

macOS · Linux
grep -l 'nvm.sh' ~/.zshrc ~/.zprofile ~/.bashrc ~/.bash_profile ~/.profile 2>/dev/null

The installer picks the file from your login shell ($SHELL) and from the files that already exist. It never creates one:

  • zsh: ~/.zshrc if it exists, otherwise ~/.zprofile. With neither, it takes the first of ~/.profile, ~/.bashrc, ~/.bash_profile that exists, and zsh reads none of those. That is the classic Mac case: a new Mac has no ~/.zshrc, so the lines land in a leftover bash file, or nowhere.
  • bash: ~/.bashrc, otherwise ~/.bash_profile, otherwise the same fallback list.
  • No file at all: it prints => Profile not found. Tried ~/.bashrc, ~/.bash_profile, ~/.zprofile, ~/.zshrc, and ~/.profile. and writes nothing.

Lines in ~/.zprofile work in Terminal, which opens login shells, but not in a zsh started inside another shell. To fix any of these, create the right file and run the installer again with PROFILE pointing at it. It updates the existing install in place and adds the lines:

macOS · zsh
touch ~/.zshrc
curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.40.8/install.sh | PROFILE="$HOME/.zshrc" bash

v0.40.8 is current as of 12 Oct 2026; the nvm README names the newest. Pasting the three lines into ~/.zshrc does the same.

“-bash: nvm: command not found” after an SSH login

An SSH login starts bash as a login shell, which reads the first of ~/.bash_profile, ~/.bash_login and ~/.profile it finds, but not ~/.bashrc, where nvm wrote its lines. Debian and Ubuntu’s stock ~/.profile loads ~/.bashrc. A ~/.bash_profile you or a tool created stops ~/.profile from being read at all. Add this line to it:

~/.bash_profile
[ -f ~/.bashrc ] && . ~/.bashrc

“bash: line 1: nvm: command not found” in SSH commands and deploy scripts

nvm works when you log in, but ssh USER@SERVER_IP 'nvm use 24', a CI deploy step or sudo -iu USER nvm ls fails. On Debian and Ubuntu, bash does read ~/.bashrc for commands run over SSH. But the stock file begins with a case $- in block that returns immediately when the shell is not interactive, and the installer appended nvm’s lines below it. Load nvm explicitly in the command:

ssh · CI deploy step
ssh USER@SERVER_IP '. "$HOME/.nvm/nvm.sh" && nvm use 24 && cd /srv/app && npm ci'

The single quotes matter: $HOME must expand on the server, not on your machine. Moving the three lines above the case block also works, but then every SSH command and script of that user that reads ~/.bashrc loads nvm first.

cron and systemd: use nvm-exec

cron runs each line with /bin/sh and reads no startup file, so the job reports /bin/sh: 1: nvm: not found. Adding source ~/.nvm/nvm.sh to the line gives /bin/sh: 1: source: not found: /bin/sh on Debian and Ubuntu is dash, which only knows the POSIX form .. The cleaner fix is nvm-exec, a small script the installer puts next to nvm.sh in ~/.nvm. It loads nvm, switches to the version in NODE_VERSION (or in .nvmrc) and runs the rest of the line:

crontab -e
0 3 * * * cd /srv/app && NODE_VERSION=24 /home/USER/.nvm/nvm-exec node job.js

NODE_VERSION=24 picks the newest 24.x you have installed, so the job survives an upgrade. A full path such as ~/.nvm/versions/node/v24.x.y/bin/node breaks as soon as you remove that version. If no matching version is installed, nvm-exec exits with 127 and prints N/A: version "v24" is not yet installed. To check the schedule itself, paste it into the cron expression generator.

systemd looks up a bare ExecStart=nvm … or ExecStart=node … only in system directories, where an nvm install never is. The service then fails with status=203/EXEC, and systemd-analyze verify warns Command node is not executable: No such file or directory. Use nvm-exec there too, and run the service as the user who owns ~/.nvm:

/etc/systemd/system/myapp.service
[Unit]
Description=Node app
After=network.target

[Service]
User=USER
WorkingDirectory=/srv/app
Environment=NODE_VERSION=24
ExecStart=/home/USER/.nvm/nvm-exec node server.js
Restart=on-failure

[Install]
WantedBy=multi-user.target

Do not set ProtectHome=yes or tmpfs in such a unit: both hide /home, and with it nvm. For a production service, Node.js from NodeSource in /usr/bin is simpler still: no per-user PATH at all. The commands are in the npx guide, and the rest of the unit in How to create a systemd service.

A cron job that still does nothing? See Cron job not running.

“sudo: nvm: command not found”

sudo starts programs, and nvm is a function inside your shell, so there is nothing for sudo to start. You do not need it either: nvm keeps Node and global npm packages in your home directory, so npm install -g works without sudo. If root really needs your Node, pass the path: sudo "$(command -v node)" script.js, or install a system-wide Node for it.

Docker

Each RUN line in a Dockerfile is a fresh /bin/sh -c that reads no profile, hence /bin/sh: 1: nvm: not found. Use an official node image instead: its tag pins the major version, and node, npm and npx are already on the PATH (example in the npx guide). If you need nvm in a container anyway, nvm’s README shows a BASH_ENV setup for it.

Installed with Homebrew

brew install nvm puts nvm.sh into /opt/homebrew/opt/nvm, but no nvm command into /opt/homebrew/bin, so zsh keeps saying command not found until you add the lines from the formula’s caveats (brew info nvm shows them). Create the directory and add these lines to ~/.zshrc:

macOS · Homebrew nvm
mkdir -p ~/.nvm
~/.zshrc
export NVM_DIR="$HOME/.nvm"
[ -s "/opt/homebrew/opt/nvm/nvm.sh" ] && \. "/opt/homebrew/opt/nvm/nvm.sh"  # This loads nvm
[ -s "/opt/homebrew/opt/nvm/etc/bash_completion.d/nvm" ] && \. "/opt/homebrew/opt/nvm/etc/bash_completion.d/nvm"  # This loads nvm bash_completion

fish: “fish: Unknown command: nvm”

nvm is written for POSIX shells: bash, zsh, dash and ksh. fish cannot load nvm.sh, and nvm does not support it. The usual answer is nvm.fish, a separate tool with similar commands. It keeps its Node versions in ~/.local/share/nvm, apart from ~/.nvm:

fish
fisher install jorgebucaran/nvm.fish
nvm install lts

nvm install activates the version only in the current shell. For new shells, set the nvm_default_version variable as its README shows.

Windows: nvm-windows is a different project

nvm does not run in cmd or PowerShell. Without a version manager installed, cmd says 'nvm' is not recognized as an internal or external command, and PowerShell 7 says The term 'nvm' is not recognized as a name of a cmdlet, function, script file, or executable program. What Windows users install is NVM for Windows, a separate program with its own installer and a similar but not identical syntax: nvm install lts, then nvm use lts. Three things trip people up:

  • Terminals that were open during the install keep the old PATH. Close them and open a new one.
  • Its README asks you to uninstall any existing Node.js first; otherwise nvm use appears to do nothing.
  • nvm install and nvm use create symlinks, which usually needs an administrator shell.

In WSL you are on Linux: use the nvm from this article there, not nvm-windows.

nvm works, but node is not found

Every new shell starts with the version named by the default alias. If nvm ls shows a line like default -> 24 (-> N/A), the default points at a version you do not have. Install one with nvm install 24, or point the alias at an installed version with nvm alias default VERSION. A prefix line in ~/.npmrc also stops nvm from switching. nvm then says the file has a `globalconfig` and/or a `prefix` setting, which are incompatible with nvm. Remove that line, or run the nvm use --delete-prefix command it prints. More on Node missing in new shells is in the npx guide.

Check that it works

new terminal
command -v nvm
nvm --version
nvm current

You should see nvm, a version number such as 0.40.8, and the active Node version. nvm current prints none when no version is active yet. Repeat the test where nvm failed before: in a new terminal, over SSH, in the cron job. If a command still exits with 127, the exit code 127 guide covers the general case.

For a Node app that runs around the clock, a Linux VPS gives you root over SSH for the system-wide Node.js a service needs.

Frequently asked

Why do I get 'nvm: command not found' right after installing nvm?

The installer runs in its own bash process and cannot load nvm into the shell you started it from. Open a new terminal, or run source ~/.zshrc (zsh) or source ~/.bashrc (bash). If a new terminal still fails, the installer wrote its lines to a file your shell does not read, which is common on Macs without a ~/.zshrc: create that file and add nvm's three lines to it.

Why does nvm work in my terminal but not with sudo, cron or ssh?

nvm is a shell function loaded by your startup files. sudo can only start programs, cron runs jobs with /bin/sh without reading any startup file, and a command sent over ssh runs in a non-interactive shell that stops at the top of Debian's and Ubuntu's stock ~/.bashrc. Load nvm explicitly with . "$HOME/.nvm/nvm.sh", or run Node through ~/.nvm/nvm-exec with NODE_VERSION set.

Is nvm-windows the same as nvm?

No. nvm is a POSIX shell script for macOS, Linux and WSL. NVM for Windows is a separate program with its own installer and similar but not identical commands, such as nvm install lts and nvm use lts, and it usually needs an administrator shell to switch versions. Use it in cmd or PowerShell; inside WSL, use the regular nvm.

Related articles

All troubleshooting