NodeJS项目通过Github Action SSH部署时PM2启动ESM模块报错
NodeJS ESM项目通过Github Action部署PM2启动失败问题解决
问题背景
我在NodeJS项目中通过指定package.json的type字段启用ES模块(ESM),同时为PM2创建了ecosystem.config.cjs配置文件(适配PM2的CommonJS规范)。配置文件内容:
module.exports = { apps: [{ name: "app-name", script: "./index.mjs", }] }
项目目录结构:
project-root/ ├── node_modules/ ├── .env ├── .gitignore ├── ecosystem.config.cjs ├── index.mjs └── package.json
通过Github Action的SSH Remote Commands部署到DigitalOcean Droplet,部署脚本:
- name: SSH Remote Commands uses: appleboy/ssh-action@v1.0.0 with: host: xxx.xxx.xxx.xxx username: root key: ${{ secrets.SSH_KEY }} script: | pm2 stop ${{ env.REPO_NAME }} pm2 flush ${{ env.REPO_NAME }} pm2 start ecosystem.config.cjs exit
部署后出现错误:
Error [ERR_REQUIRE_ESM]: Must use import to load ES Module: /root/nodejs-app/index.mjs at Module.load (internal/modules/cjs/loader.js:861:11) at Function.Module._load (internal/modules/cjs/loader.js:708:14) at Object.<anonymous> (/usr/local/lib/node_modules/pm2/lib/ProcessContainerFork.js:33:23) at Module._compile (internal/modules/cjs/loader.js:999:30) at Object.Module._extensions..js (internal/modules/cjs/loader.js:1027:10) at Module.load (internal/modules/cjs/loader.js:863:32) at Function.Module._load (internal/modules/cjs/loader.js:708:14) at Function.executeUserEntryPoint [as runMain] (internal/modules/cjs/loader.js:60:12) at internal/main/run_main_module.js:17:47 { code: 'ERR_REQUIRE_ESM' }
但手动在VPS执行PM2启动命令完全正常。
问题分析与解决方案
1. 工作目录不匹配
Github Action的SSH脚本默认执行路径可能不是项目根目录,导致PM2无法读取package.json中的type配置,无法识别ESM模块。手动执行时你处于项目根目录,但CI脚本可能在其他路径运行。
- 修复:在脚本中先切换到项目根目录:
script: | cd /root/nodejs-app pm2 stop ${{ env.REPO_NAME }} pm2 flush ${{ env.REPO_NAME }} pm2 start ecosystem.config.cjs exit
2. PM2进程残留或缓存
CI执行pm2 stop后可能残留旧的进程容器,这些容器仍以CommonJS模式运行,导致新进程启动时沿用旧的模块规则。手动执行时已清理干净残留进程。
- 修复:强制删除旧进程并清理缓存:
script: | cd /root/nodejs-app pm2 delete ${{ env.REPO_NAME }} || true pm2 flush ${{ env.REPO_NAME }} || true pm2 start ecosystem.config.cjs exit
3. Node.js环境变量差异
手动执行时的Shell环境变量(如NODE_OPTIONS)与CI脚本环境不同,CI环境可能缺少ESM相关配置。
- 修复:
- 方法一:在启动命令前添加环境变量:
script: | cd /root/nodejs-app pm2 delete ${{ env.REPO_NAME }} || true NODE_OPTIONS="--experimental-specifier-resolution=node" pm2 start ecosystem.config.cjs exit- 方法二:修改
ecosystem.config.cjs添加interpreter_args:
module.exports = { apps: [{ name: "app-name", script: "./index.mjs", interpreter_args: "--experimental-specifier-resolution=node" }] }
4. PM2版本不一致
VPS上手动使用的PM2版本与CI脚本调用的版本可能不同,旧版本PM2对ESM的支持存在缺陷。
- 修复:
- 使用项目本地安装的PM2:
script: | cd /root/nodejs-app pm2 delete ${{ env.REPO_NAME }} || true npx pm2 start ecosystem.config.cjs exit- 升级VPS上的PM2到最新版本:
script: | npm install -g pm2@latest cd /root/nodejs-app pm2 delete ${{ env.REPO_NAME }} || true pm2 start ecosystem.config.cjs exit
内容的提问来源于stack exchange,提问作者hh54188
相关产品推荐
相关产品推荐

