Обновление Node под pm2 без сюрпризов
TL;DR: При обновлении Node.js через nvm под pm2: привяжите nvm alias default к мажорной версии, поправьте сохранённый PATH в ~/.pm2/dump.pm2 для приложений в fork mode, перегенерируйте systemd-юнит pm2 с новым бинарником node и проверьте readlink /proc/PID/exe, прежде чем удалять старую версию.
Вышел security-релиз, и я перевёл несколько pm2-приложений на свежую версию Node через nvm. Сам nvm install занял минуту. А вот найти все места, где пряталась старая версия, вышло заметно дольше. Ниже порядок, который сработал с одним рестартом на каждое приложение.
1. Привяжите default в nvm к мажорной версии
Если default у вас указывает на lts/*, nvm переписывает этот алиас при каждом запросе списка версий. Как только новая ветка становится LTS, default начинает указывать на версию, которую вы не ставили, и новые шеллы стартуют вообще без node.
nvm install 24 --reinstall-packages-from=22
nvm alias default 24
--reinstall-packages-from ставит ту же версию pm2, что была, а не последнюю. Здесь это ровно то, что нужно.
2. Приложения в fork mode помнят старый PATH
pm2 сохраняет окружение каждого приложения в ~/.pm2/dump.pm2. Приложения в fork mode запускают node через сохранённый PATH, поэтому после рестарта демона они тихо продолжают работать на старом бинарнике. Приложения в cluster mode используют node самого демона.
Сделайте бэкап дампа и замените старую версию в PATH, NVM_BIN, NVM_INC и _. pm2 restart --update-env тоже подойдёт, но если запускать его неинтерактивной командой по SSH, он подхватит урезанный PATH этого шелла.
3. Перегенерируйте systemd-юнит
В юните, который создаёт pm2 startup, путь к node зашит намертво. Сгенерируйте его заново с новым бинарником:
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
pm2 update по SSH для этого не подходит: он перезапустит демон внутри scope вашей SSH-сессии, а не под systemd.
4. Проверьте и только потом удаляйте
for p in $(pgrep -f node); do readlink /proc/$p/exe; done | sort | uniq -c
pm2 save
nvm uninstall — только когда на старую версию больше ничего не ссылается.