PM2配置的核心答案:在Node.js项目根目录创建ecosystem.config.js文件,用pm2 start加载配置,就能实现进程守护、负载均衡、开机自启和日志管理,一句话概括就是“写一次配置,处处自动运行”。

PM2是目前使用最广泛的Node.js进程管理工具,没有之一,它解决的核心痛点很实在:Node应用崩溃了没人管、重启要手动敲命令、服务器重启后服务忘开、日志散落各处找不到,这些问题靠一份好的配置文件就能根治,本文从零开始扒一遍PM2配置的底层逻辑、常用参数、实战场景和排错思路,覆盖pm2配置文件怎么用、pm2 环境变量配置、pm2 开机自启怎么设置这些高频需求。
PM2配置文件的基础结构:ecosystem.config.js到底怎么写
PM2支持两种配置文件格式:ecosystem.config.js和ecosystem.json,社区推荐使用JS格式,因为支持注释、支持动态逻辑、不需要纠结JSON语法严格性,先看一份最基础也是最常见的配置文件长什么样:
module.exports = {
apps: [
{
name: 'blog-server',
script: './src/app.js',
instances: 2,
exec_mode: 'cluster',
watch: false,
max_memory_restart: '200M',
env: {
NODE_ENV: 'production',
PORT: 3000
}
}
]
};
这份配置包含了几个关键信息:应用名blog-server、入口文件./src/app.js、启动2个实例跑集群模式、单实例内存超过200MB自动重启、注入生产环境变量。
为什么说script路径是配置的灵魂
script字段指定启动脚本路径,在实际项目中常常踩坑的是路径问题,路径不是相对于当前终端所在的目录,而是相对于配置文件所在的目录,假设你的配置文件放在/home/user/www/blog/ecosystem.config.js,那么script: './src/app.js'实际指向/home/user/www/blog/src/app.js。
业内专家指出,大家在配置时最容易忽略的一个细节是:如果把项目从一台服务器迁移到另一台,路径变了但配置文件没同步,应用会一直报module not found,排查半天还以为是PM2出问题。
instances与exec_mode:单实例还是多实例
instances的取值有三种语义:
- 固定数字,比如
instances: 4表示起4个进程 'max'表示根据服务器CPU核心数启动对应数量的实例0表示自动检测,效果等同于'max'
需要注意的是,单实例模式配合exec_mode: 'fork'适用于绝大多数简单的Node服务;多实例必须配合exec_mode: 'cluster'才能生效,Cluster模式由PM2自动做负载均衡,把请求分发到各个进程上。
PM2配置环境变量:env和env_production的区别
pm2 环境变量配置是上线生产环境前必过的一关,很多人直接在系统层面export变量,这种方式的问题是:服务器重启之后变量丢失、多项目之间变量互相污染,正确做法是把环境变量写进PM2配置里。
环境变量的分层设计
看下面的配置片段:
apps: [
{
name: 'api-server',
script: './dist/index.js',
env: {
NODE_ENV: 'development',
DEBUG: 'api:'
},
env_production: {
NODE_ENV: 'production',
API_BASE_URL: 'https://api.example.com'
},
env_staging: {
NODE_ENV: 'staging',
API_BASE_URL: 'https://staging-api.example.com'
}
}
]
启动方式决定注入哪一套变量:
pm2 start ecosystem.config.js注入envpm2 start ecosystem.config.js --env production注入env_productionpm2 start ecosystem.config.js --env staging注入env_staging
这种设计的价值在于:开发环境、测试环境、生产环境共用一份配置文件,变量自动切换,不用改代码也不用记一堆命令行参数。行业共识认为,配置分离做得越干净,后期运维越省心,环境差异导致的问题能减少很大比例。
敏感信息别硬编码
数据库密码、API密钥这类敏感信息不应直接写进配置文件,虽然config.js是项目内部文件,但一段时间后仍会被提交到Git仓库,推荐的替代方案:
- 配合dotenv使用,在config.js中读取
.env文件里的变量 - 使用
process.env从系统环境变量中取值,配置文件只做映射
env_production: {
NODE_ENV: 'production',
DB_HOST: process.env.DB_HOST,
DB_PASSWORD: process.env.DB_PASSWORD
}
这样生产服务器上只需在系统层面配置一次环境变量,项目代码和配置文件中都不会暴露明文密码。

PM2配置文件中的日志管理:切割、路径与轮转
PM2默认会把应用的stdout和stderr写入~/.pm2/logs/目录,名称为应用名-out.log和应用名-error.log,但这个默认方案有几个硬伤:没有按天切割、日志无限增长、不便于对接日志分析平台。
日志参数配置
在app配置中添加以下参数:
{
name: 'api-server',
script: './server.js',
out_file: './logs/out.log',
error_file: './logs/err.log',
merge_logs: true,
log_date_format: 'YYYY-MM-DD HH:mm:ss Z',
max_size: '50M',
retain: 7
}
out_file和error_file分别指定标准输出和错误日志的路径。merge_logs在集群模式下把多个进程的日志合并到一个文件,避免日志碎成好几块。log_date_format给每条日志加时间戳前缀,max_size和retain控制文件上限和保留份数,这些参数全部由配置文件管理,不需要额外安装模块。
生产环境日志轮转实操
虽说PM2内置了max_size参数,但多数生产项目仍搭配pm2-logrotate模块使用,原因在于:PM2的max_size是“文件达到大小后重启写入新文件”,而logrotate模块支持按大小、按时间两种轮转策略,还支持压缩旧日志,安装与配置方法:
pm2 install pm2-logrotate pm2 set pm2-logrotate:max_size 100M pm2 set pm2-logrotate:retain 14 pm2 set pm2-logrotate:compress true
这样设置后,单个日志文件超过100MB自动切割,保留14份,历史日志压缩存储,磁盘空间压力大幅缓解。
PM2开机自启与进程恢复:服务器重启后服务自动上线
PM2配置不仅仅管运行时的东西,还管“死后复活”的机制,部署在云服务器上的Node应用,最怕的一件事就是机房断电或系统自动更新重启,人不在电脑前,服务就瘫了。
开机自启的配置步骤
PM2提供一个高度集成的命令解决这个问题:
pm2 save pm2 startup
pm2 startup执行后,终端会输出一段systemctl兼容的配置命令,复制输出内容后在root权限下执行一次,之后PM2会注册成一个系统服务,服务器开机时自动拉起PM2守护进程,再由PM2拉起你pm2 save中保存的所有应用,这段操作走完,pm2开机自启配置就基于系统层面完成了。
自启后的自愈能力
PM2本身还有一套“自愈”机制,来自动处理运行中崩溃的应用:
- 崩溃退出的进程在
restart_delay指定的毫秒数后自动重启 - 代码文件变化且开启
watch: true时自动同步重启 - 内存超限触发
max_memory_restart自动重启
{
name: 'worker',
script: './worker.js',
max_memory_restart: '300M',
restart_delay: 5000,
min_uptime: '10s'
}
min_uptime的意义在于:如果应用启动后10秒内就崩溃,PM2判定为“启动失败循环”,不会无限重启浪费资源,而是标记为异常停止,方便人工介入排查。
PM2集群模式与性能调优:自动负载均衡
Node.js单线程模型决定了单进程只能使用一个CPU核心,服务器的CPU在闲着,而你的Node应用在排队,这是常见的资源浪费场景,PM2的cluster模式解决的就是这个错配问题。
cluster模式工作原理
开启cluster后,PM2作为master进程创建多个worker进程,每个worker监听同一个端口,PM2内部使用Round-robin算法把请求轮询分配给各worker,实现负载均衡的语义。
{
name: 'server',
script: './server.js',
instances: 'max',
exec_mode: 'cluster',
wait_ready: true,
listen_timeout: 5000,
kill_timeout: 3000
}
wait_ready配合process.send('ready')在应用代码中显式上报就绪状态,PM2收到后再分发新请求,避免重启瞬间丢请求。listen_timeout和kill_timeout分别控制监听超时和强制回收时间,保障平滑重启。

PM2 配置文件怎么用:负载均衡调优参考
集群模式下比较关键的调优点有两个方向:
- 实例数不是越多越好,实例数超过CPU核心数后反而增加上下文切换开销
- 无状态应用(session存redis、文件存OSS)适合cluster模式;有状态应用部署在同一台机器上时需要特别注意共享资源竞争
在实际运维场景中,多数团队会选择让instances与CPU核心数对齐,然后通过压测工具判断是否需要横向扩缩。
PM2部署常见问题与解决方案
PM2 配置过程中日志不输出
日志不输出的原因主要有几种:
- 代码中未写入
stdout,用了自定义logger且未配置transport - 日志路径配置错误,
out_file指向了一个不存在的目录 - 磁盘满了导致写入失败
排查顺序建议为:先pm2 logs看是否有实时流,再检查配置文件路径是否为绝对路径,最后确认磁盘可用空间。
环境变量没注入成功
配置文件定义了env_production,但用pm2 restart时没加--env production,导致生产环境的变量没有打进进程,PM2重启时如果不带--env参数,会沿用当前项目的环境变量,而不是重新读取配置文件,正确的操作是:修改配置文件后必须执行pm2 restart更新应用,但想切换环境变量需要执行pm2 start加载一次新的配置。
与node项目 pm2 部署 配置相关的平滑部署实践
在Node项目的CI/CD流水线上,PM2命令的执行顺序需要谨慎规划,很多开发者在构建后直接执行pm2 reload,但如果新代码构建失败或目录结构发生变化,reload后服务就起不来了,推荐的做法:
pm2 describe blog-server > /dev/null 2>&1 && pm2 reload ecosystem.config.js --update-env || pm2 start ecosystem.config.js
这条命令的逻辑是:先检查应用是否存在,存在则reload并更新环境变量,不存在则直接start,这样配合Jenkins或GitLab CI,可以实现较为稳健的PM2部署流程。
Q&A:pm2配置常见疑问解答
pm2配置文件里name重名会怎样?
如果两个应用的name字段相同,后启动的会覆盖前一个,导致前一个应用变成errored状态,建议每个应用的name保持全局唯一,使用项目名或服务名做命名规范。
pm2配置文件中的watch会导致重启循环吗?
会,当watch为true时,PM2监控指定目录文件变化,如果被监控目录包含日志输出目录,日志写入会不断触发重启信号,造成死循环,务必在watch配置中排除日志目录、node_modules和.git目录,例如设置watch: ['src', 'config']。
pm2失败重启次数有没有上限?
PM2默认不限制重启次数,如果应用陷入“启动即崩溃”的循环,CPU占用会持续飙升,建议配置max_restarts: 10表示1分钟内最多重启10次,超过后PM2停止尝试,将进程标记为stopped状态并等待人工介入。
