惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

WordPress大学
WordPress大学
博客园 - 司徒正美
Last Week in AI
Last Week in AI
博客园 - 聂微东
Jina AI
Jina AI
月光博客
月光博客
爱范儿
爱范儿
美团技术团队
钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知
Hugging Face - Blog
Hugging Face - Blog
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
博客园 - 叶小钗
T
Tailwind CSS Blog
博客园 - 【当耐特】
让小产品的独立变现更简单 - ezindie.com
让小产品的独立变现更简单 - ezindie.com
Apple Machine Learning Research
Apple Machine Learning Research
有赞技术团队
有赞技术团队
罗磊的独立博客
小众软件
小众软件
雷峰网
雷峰网
IT之家
IT之家
大猫的无限游戏
大猫的无限游戏
freeCodeCamp Programming Tutorials: Python, JavaScript, Git & More
V
Visual Studio Blog

木灵鱼儿

18 NestJS 基于 Docker 的 NestJS + Prisma + Playwright 生产环境容器化实践 - 木灵鱼儿 17 NestJS 使用 CoreModule 实现基础设施与业务域的彻底解耦 - 木灵鱼儿 VS Code BYOK 配置生成器 - 木灵鱼儿 2026 最新禁止浏览器密码管理器弹出教程 - 木灵鱼儿 16 NestJS 企业级 RBAC 权限控制体系 - 木灵鱼儿 15 NestJS 统一响应体设计(信封模式) - 木灵鱼儿 14 NestJS 生产级错误过滤方案 - 木灵鱼儿 13 NestJS 集成 TypeORM 完全指南 - 木灵鱼儿 12 NestJS 集成 Prisma ORM 完全指南(Prisma v7) - 木灵鱼儿 11 NestJS 注册与登录接口的密码安全设计 - 木灵鱼儿 10 NestJS JWT 身份验证完全指南 - 木灵鱼儿 09 NestJS 使用 @nestjs-swagger 生成 API 文档 - 木灵鱼儿 08 NestJS DTO 校验、Entity 脱敏与 Mapped Types 实战(TypeORM & Prisma) - 木灵鱼儿 07 NestJS 使用Caching缓存(cache-manager) - 木灵鱼儿 06 NestJS 使用Redis - 木灵鱼儿 05 Nestjs 高性能构建之SWC与Typescript7选型 - 木灵鱼儿 04 Nestjs API版本控制策略 - 木灵鱼儿 NestJS 正确处理日志 - 木灵鱼儿 NestJS config模块扩展用法.md - 木灵鱼儿 宝塔如何给网站一次性绑定两个域名 - 木灵鱼儿 NestJS 环境变量与配置管理(Config 模块) - 木灵鱼儿 如何使用 FNM 自动切换不同项目的 Node.js 版本 - 木灵鱼儿 如何使用 Node.js Corepack 自动锁定和切换项目包管理器版本 - 木灵鱼儿 日常开发中的 void:从忽略 Promise 到理解 JavaScript 历史写法 - 木灵鱼儿 git 进阶指南:如何用 Git Worktree 完美应对紧急 Bug 与多分支联调 - 木灵鱼儿 了解并解决 Git 大小写“无感”的幽灵 Bug - 木灵鱼儿 从零开始:手把手教你封装一个企业级 Axios 请求模块 - 木灵鱼儿 03-Vue Query 高级进阶:应对复杂业务场景的硬核套路 - 木灵鱼儿 02-Vue Query 快速入门:从零构建你的第一个声明式查询 - 木灵鱼儿 01-异步状态管理新范式:为什么在 Vue 3 中使用 vue-query? - 木灵鱼儿
宝塔环境下 PM2 进程守护与多版本 Node.js 持久化方案 - 木灵...
木灵鱼儿 · 2026-09-20 · via 木灵鱼儿

前言

宝塔里通过node管理器安装了对应的node版本后,我们在其对应的node模块管理器中安装pm2模块,虽然pm2模块安装后可以正常使用,但是每次服务器重启,对应的node项目并没有启动,一开始我以为是宝塔的问题。

后来发现并不是这样,为此记录一下,方便后续使用。


运行机制解析:进程守护与开机自启的边界

解决该问题的关键,在于区分两层不同维度的守护机制:

  1. 应用级进程监控(Runtime Supervision)

    • 机制:当 Node.js 应用触发未捕获异常退出时,内存中运行的 PM2 主守护进程(PM2 Daemon)会自动拉起应用。
    • 范围:仅在操作系统运行期有效,由 PM2 原生提供。
  2. 系统级服务持久化(System Init Supervision)

    • 机制:操作系统重启时,内核清空内存空间并初始化系统服务。操作系统必须通过 Init 系统(如 Systemd)主动调用 PM2 的二进制程序并恢复先前的运行上下文。
    • 成因:宝塔面板中安装 PM2 模块仅完成了 Node 全局包的安装,并未向系统级服务管理器(Systemd)注册服务单元。因此,系统重启后 PM2 守护进程并未被系统加载,应用自然无法恢复。

要实现完全持久化,必须显式完成两步:持久化当前运行列表(Dump)注册系统服务(Register Service)


PM2 状态持久化与 Systemd 注册

在应用正常启动的前提下,登录服务器终端执行以下标准操作:

1. 持久化当前进程快照

pm2 save

该命令将当前正在运行的进程列表、运行参数及路径序列化至磁盘(通常保存于 ~/.pm2/dump.pm2)。若未执行该操作,即便服务自启,PM2 恢复的目标列表也将为空。

2. 注册并启用 Systemd 服务单元

在 root 权限下执行:

pm2 startup

PM2 会自动检测操作系统发行版,并生成适配的环境变量与服务文件。

  • 若终端输出以 sudo env PATH=... 开头的命令,请完整复制并在终端中执行
  • 该命令将创建 /etc/systemd/system/pm2-root.service 文件,并将当前 Node 环境变量与服务配置固化写入。

3. 服务可用性验证

systemctl status pm2-root

确认服务指标:

  • Loaded: ...; enabled;:确认服务已进入系统开机自启队列。
  • Active: active (running):确认系统服务处于平稳运行状态。

多 Node.js 版本环境下的服务失效分析

在多项目共存的环境中,若频繁在宝塔中切换 Node.js 默认版本,会出现服务链断裂的情况。

1. 物理路径隔离

宝塔面板的 Node 版本管理器采用物理目录隔离机制:

  • Node v18 路径:/www/server/nodejs/v18.x.x/bin/
  • Node v20 路径:/www/server/nodejs/v20.x.x/bin/

当在宝塔中切换全局版本时,新版本环境中并不包含旧版本安装的全局模块。若在新版本下未重新安装 PM2,终端将直接报错 command not found

2. 固化 PATH 引发的服务崩溃

pm2 startup 在生成 pm2-root.service 时,会将执行命令时的 Node 绝对路径硬编码到配置中。
如果切换了系统全局 Node 版本,或者移除了先前生成服务的 Node 版本:

  • Systemd 在启动 pm2-root.service 时将因路径失效而直接进入 inactive (dead)failed 状态。
  • 因此,若通过常规方式切换全局 Node 环境,必须重新安装 PM2,并重新执行 pm2 unstartuppm2 startup 重构系统服务。

单一守护进程与多运行时调度实践

为了避免在多版本共存时频繁重构系统服务,推荐采用“单管理核心 + 多运行时调度”方案。

核心设计原则

选择一个稳定的 Node.js LTS 版本作为底座,只构建唯一的系统级 PM2 守护服务;具体的业务应用通过指定 Node 执行路径(Interpreter)实现运行时隔离。

实施步骤

1. 锁定基础环境与系统服务

在宝塔中选定一个长期支持的 Node 版本(如 Node 20),在此版本下安装 PM2,并按第二节的步骤完成 pm2 savepm2 startup。后续不再变动全局 PM2 服务

2. 定位各版本的 Node 执行文件绝对路径

宝塔默认安装路径规则如下:

  • Node 16:/www/server/nodejs/v16.x.x/bin/node
  • Node 18:/www/server/nodejs/v18.x.x/bin/node

3. 通过 Interpreter 参数指派运行时

  • CLI 命令行调度方式:

    # 调度至 Node 16 运行
    pm2 start app.js --name "service-a" --interpreter /www/server/nodejs/v16.x.x/bin/node
    
    # 调度至 Node 18 运行
    pm2 start index.js --name "service-b" --interpreter /www/server/nodejs/v18.x.x/bin/node
  • 配置文件(ecosystem.config.js)调度方式(推荐):

    module.exports = {
        apps: [
            {
                name: "legacy-api",
                script: "./server.js",
                interpreter: "/www/server/nodejs/v16.20.2/bin/node",
            },
            {
                name: "modern-service",
                script: "./app.js",
                interpreter: "/www/server/nodejs/v18.19.0/bin/node",
            },
        ],
    };

4. 固化调度状态

项目启动完成后,更新快照:

pm2 save

版权申明

本文系作者 @木灵鱼儿 原创发布在木灵鱼儿站点。未经许可,禁止转载。