04 PM2、NVM 与 systemd

1. 为什么 NVM 会影响 PM2

NVM 把不同 Node.js 版本安装到不同目录,例如:

/home/deploy/.nvm/versions/node/v20.x.x/bin/node
/home/deploy/.nvm/versions/node/v22.x.x/bin/node

在 Node 22 下执行:

nvm use 22
npm install --global pm2

PM2 通常也安装在 Node 22 对应目录。切换版本后:

nvm use 20

可能发现 pm2 命令不存在,因为 Node 20 的全局包目录是另一套。

2. 生产服务器的简单策略

先选定一个经过项目验证的 Node.js LTS 主版本,然后保持稳定:

nvm install 22
nvm alias default 22
nvm use 22
node --version
npm --version
npm install --global pm2@latest
pm2 --version

可以在项目中创建 .nvmrc

22

以后进入项目后:

nvm use

.nvmrc 固定的是项目期望的 Node 版本,不会自动替你升级服务器或重建原生依赖。

3. 检查实际路径

which node
which npm
which pm2
node --version
pm2 --version

示例输出可能是:

/home/deploy/.nvm/versions/node/v22.0.0/bin/node
/home/deploy/.nvm/versions/node/v22.0.0/bin/pm2

版本号只是示例,不要把它写死进别人的服务器命令。

4. PM2 daemon 使用哪个 Node.js

PM2 由哪个 Node.js 启动,取决于当时 shell 的 PATH。应用默认也会使用相应 Node 解释器。

查看应用详情:

pm2 show nloop-api

升级 Node 后,仅执行 nvm use 不会神奇地替换已经运行的 PM2 daemon 和 systemd 脚本。需要明确重建启动配置。

5. pm2 startup 做了什么

PM2 daemon 本身是普通用户进程。服务器重启后,需要 Linux 初始化系统重新启动它。

以常见 systemd Linux 为例:

服务器开机
  → systemd 启动 pm2-deploy.service
  → PM2 daemon 启动
  → PM2 读取保存的进程列表
  → nloop-api 恢复运行

先以部署用户运行,不要直接加 sudo:

pm2 startup

PM2 会检测 init system,并输出一条类似命令:

sudo env PATH=$PATH:/home/deploy/.nvm/versions/node/v22.x.x/bin \
  pm2 startup systemd -u deploy --hp /home/deploy

应复制执行 PM2 在当前服务器上实际输出的命令,因为其中包含:

不要机械复制教程中的 v22.x.x

6. 保存要恢复的应用

确认应用状态正常后:

pm2 list
pm2 save

以后修改了 PM2 进程列表,例如新增或删除 worker,应重新:

pm2 save

否则 systemd 启动时恢复的可能仍是旧快照。

手动测试恢复:

pm2 resurrect

不要在正在提供服务的生产环境随意 pm2 kill 只为测试,因为它会停止当前用户管理的全部应用。

7. 使用 systemctl 管理 PM2 服务

默认服务名通常类似:

pm2-<Linux用户名>

部署用户为 deploy 时:

sudo systemctl status pm2-deploy
sudo systemctl start pm2-deploy
sudo systemctl stop pm2-deploy
sudo systemctl restart pm2-deploy
sudo systemctl enable pm2-deploy
sudo systemctl disable pm2-deploy

查看 systemd 日志:

sudo journalctl -u pm2-deploy
sudo journalctl -u pm2-deploy -n 100 --no-pager

查看生成的服务配置:

sudo systemctl cat pm2-deploy

这对排查 NVM 路径失效特别有用。

8. 三种“停止”的层级

只停止一个应用

pm2 stop nloop-api

PM2 daemon 和其他应用继续运行。

停止整个 PM2 systemd 服务

sudo systemctl stop pm2-deploy

会影响该 PM2 服务管理的应用。

禁止开机启动

sudo systemctl disable pm2-deploy

它只取消以后开机自动启动,不一定立即停止当前服务。

stopdisable 不要混为一谈。

9. 删除 startup 配置

按官方流程:

pm2 unstartup

根据 PM2 输出执行所需命令。之后再确认:

systemctl status pm2-deploy

如果只是临时停止某个应用,不需要删除整个 startup 配置。

10. 升级 Node.js 时的推荐流程

Node 主版本升级不是简单执行一次 nvm use

1. 安装新 Node.js
2. 在新版本中安装 PM2
3. 在新版本中重新 npm ci
4. 类型检查、构建并直接运行测试
5. 删除旧 PM2 startup 配置
6. 使用新 Node 路径重新执行 pm2 startup
7. 启动应用并 pm2 save
8. 检查 systemd 配置和重启恢复

示意命令:

nvm install 24
nvm use 24
npm install --global pm2@latest

cd /var/www/nloop/current
npm ci
npm run typecheck
npm run build

pm2 unstartup
pm2 startup
pm2 start ecosystem.config.js
pm2 save

是否升级到示例版本必须结合 Node LTS、依赖兼容性和项目测试决定。

11. 常见问题

SSH 登录后 pm2: command not found

先检查 NVM 是否加载、Node 版本和路径:

command -v nvm
nvm current
which pm2

手工运行正常,开机后失败

重点检查:

sudo systemctl cat pm2-deploy
sudo journalctl -u pm2-deploy -n 100 --no-pager

systemd 不会自动执行你的交互式 shell 配置,必须依靠 startup 脚本中的明确 PATH。

pm2 list 是空的

确认当前 Linux 用户:

whoami
echo "$PM2_HOME"

很可能之前用另一个用户或 sudo pm2 启动了应用。

复盘题

  1. NVM 切换 Node 版本后,为什么全局安装的 PM2 可能消失?
  2. 为什么应执行 pm2 startup 实际输出的 sudo 命令?
  3. systemctl stopsystemctl disable 有什么区别?
  4. 为什么 Node 升级后要重新生成 PM2 startup 配置?
  5. pm2 list 为空时,为什么首先要检查当前 Linux 用户?

官方参考