基于 Express + Vite (React) 的 Node.js 项目部署至 ct8.pl (MyDevil) 踩 坑与实 践指南
ct8.pl(MyDevil 旗下的免费 Node.js 托管平台)底 层基于 FreeBSD 系统,并结合 Phusion Passenger 来管理和调度 Node.js 应用。由于其环境与常见的 Linux(Ubuntu/Debian) 搭配 Nginx + PM2 的部署模式存在较大差 异,在将使用 Vite 构建的现代 Node.js / React 全 栈应用部署上去时,极易遇到静态资源 挂载失败、模 块加载 崩 溃(ESM vs CommonJS)等问题。
本文总结了从最初的 npm start 报错到最终成功部署并运行完整的 Sing-box 订 阅管理平台的完整调试过程与实战教程。
一、 ct8.pl / MyDevil 托管环境 核心机制
在开始部署前,必须厘清 ct8.pl 的底 层运行 逻辑:
- Passenger 接管启动:ct8 并不直接运行
npm start,而是通过 Phusion Passenger 查 找项目根目录下的app.js文件并进行引导。 - 静态资源目录:Web 网关默认将请求直接映射到项目根目录下的 public/** 文件 夹。
- CommonJS 限制:Passenger 的底 层引导程序(
node-loader.js)使用 require() 加载app.js。如果项目配置了 ESM,会导致应用无法启动。 - FreeBSD 命令行:系统基于 FreeBSD,使用
sed等命令行工具时参数与 Linux 存在 细微差 异。
二、 完整部署步 骤教程
步 骤 1:本地编译与打包
确保在本地或服务器项目根目录下完成了 Vite 前端与后端服务端的打包。
执行构建命令:
| |
执行后,构建系统应在根目录下生成 dist/ 文件 夹,其中包含:
- dist/index.html 及
dist/assets/(前端静态文件) dist/server.cjs(打包后的单文件 Node.js 服务端入口)
步 骤 2:配置 package.json(关 键)
由于 Passenger 使用 CommonJS 模式的 require() 加载引导文件,若 package.json 中带有 "type": "module",Node.js 会强制将 app.js 解 析为 ES Module, 抛出 ERR_REQUIRE_ESM 错误。
在 FreeBSD 终端中运行以下命令,将项目作用域转换为 commonjs:
| |
注:FreeBSD 的 sed -i 语法必须带有空单引号 ''。
验证修改结果:
| |
步 骤 3:配置静态资源目录 public
ct8 默认读取 public/ 目录中的文件作为对外暴 露的静态资源。将编译出的 dist 前端产物 覆 盖/ 链接至 public 文件 夹:
| |
步 骤 4:编写 Passenger 引导文件 app.js
在项目根目录下创建 app.js,使 Passenger 能够正确引导并启动 dist/server.cjs 生产环境服务:
| |
步 骤 5:完善数据目录权限与环境变量
如果项目 涉及 SQLite 数据库或本地 JSON 数据写入(如 data/ 目录),需确保目录已创建且 赋权:
| |
步 骤 6:重启 Node.js 应用
在 ct8.pl 环境中,无须通过 devil 命令行工具管理单个进程,只需通过 Passenger 的 tmp/restart.txt 机制 触发平 滑重启:
| |
三、 常见报错与排查手册(Troubleshooting)
1. Error: Cannot find module ‘…/dist/server.cjs’
- 原因:运行了 npm start 但项目未 执行构建,
dist/目录不存在。 - 解决:先运行
npm run build生成构建产物。
2. Error: listen EPERM: operation not permitted 0.0.0.0:24678
- 原因:
server.ts在生产环境下误启动了 Vite 的 HMR (Hot Module Replacement) 开发服务器或 WebSocket 监听。 - 解决: 检查服务端入口 逻辑,确保在生产环境下只 挂载
express.static,不启动createViteServer,并且显式指定process.env.NODE_ENV = 'production'。
3. Error [ERR_REQUIRE_ESM]: require() of ES Module app.js … not supported
- 原因:Passenger 的 node-loader.js 使用 require() 加载 app.js,但
package.json中配置了"type": "module"。 - 解决:修改
package.json中的 “type” 字段为"commonjs"。
4. 页面返回 500 Internal Server Error 且控制台报 Unexpected token ‘<’
- 原因:Node.js 服务端 崩 溃,Nginx 返回了 HTML 格式的 500 错误页面,前端 尝试以 JSON 解 析该页面导致语法报错。
排查:查看错误日志以定位具体 崩 溃位置:
| |
四、 总结流程图
整个部署排查 链条可总结为:
打包 (npm run build) └── 生成 dist/ (含前端静态文件与 server.cjs) ├── 将静态文件 覆 盖到 public/ (解决 404/默认占位页问题) ├── package.json 修改为 “type”: “commonjs” (解决 ERR_REQUIRE_ESM) ├── 编写 app.js 引导 require(’./dist/server.cjs’) ( 适配 Passenger) └── touch tmp/restart.txt ( 触发应用重启)
按此规范配置后,Vite + Express/Node.js 项目即可在 ct8.pl 虚 拟主机环境 稳定运行。