# Upgrading Node under pm2 without the surprises

Published 2026-11-05 · Node.js, nvm, pm2, systemd, Linux · https://home.tigran.manukyan.dev/notes/node-upgrade-under-pm2.html

**TL;DR:** When upgrading Node.js with nvm under pm2, pin `nvm alias default` to a major version, update the saved PATH in ~/.pm2/dump.pm2 for fork-mode apps, regenerate the pm2 systemd unit with the new node binary, and check `readlink /proc/PID/exe` before uninstalling the old version.

A security release came out, so I moved a few pm2 apps to a newer Node with nvm. `nvm install` took a minute. Finding every place the old version was still hiding took longer. Here is the order that worked with one restart per app.

## 1. Pin the nvm default to a major version

If your default is `lts/*`, nvm rewrites that alias every time it fetches the remote list. When a new line goes LTS, `default` points at a version you never installed, and new shells start with no `node` at all.

```
nvm install 24 --reinstall-packages-from=22
nvm alias default 24
```

`--reinstall-packages-from` reinstalls the same pm2 *version* you had, not the latest one. That's what you want here.

## 2. Fork-mode apps remember the old PATH

pm2 saves each app's environment in `~/.pm2/dump.pm2`. Fork-mode apps spawn `node` through the saved `PATH`, so after a daemon restart they quietly keep running the old binary. Cluster-mode apps follow the daemon's own node.

Back up the dump, then replace the old version string in `PATH`, `NVM_BIN`, `NVM_INC` and `_`. `pm2 restart --update-env` also works, but run from a non-interactive SSH command it picks up that shell's thinner PATH.

## 3. Regenerate the systemd unit

The unit made by `pm2 startup` hard-codes the node path. Generate it again with the new binary:

```
NEW=$HOME/.nvm/versions/node/v24.x.y
sudo env PATH=$NEW/bin:$PATH $NEW/bin/node $NEW/lib/node_modules/pm2/bin/pm2 \
  startup systemd -u $USER --hp $HOME
sudo systemctl daemon-reload
sudo systemctl restart pm2-$USER
```

Avoid `pm2 update` over SSH for this. It restarts the daemon inside your SSH session's scope instead of under systemd.

## 4. Verify, then uninstall

```
for p in $(pgrep -f node); do readlink /proc/$p/exe; done | sort | uniq -c
pm2 save
```

Only when nothing points at the old version, run `nvm uninstall`.
