pm2
PM2 is a host-level process manager that starts applications in the background, restarts crashed processes, captures stdout and stderr, reports CPU and memory use, and can restore a saved process list after reboot. For Node and Bun HTTP services it can run multiple cluster workers behind one port and coordinate reloads; it can also supervise Python, Ruby, shell scripts, and executables in fork mode. The npm package provides the daemon, CLI, programmatic API, terminal monitor, startup-script generator, deployment helper, and container-focused pm2-runtime command.
PM2 remains a capable all-in-one supervisor for small fleets of directly managed servers, and version 7 shows substantial current maintenance. Skip it when an init system or orchestrator already owns lifecycle, and plan logging, readiness, shutdown, state, and startup persistence before calling the deployment production-ready.
Use it if
- You run one or more long-lived Node or Bun services directly on a Linux host or VM and want restart, logs, metrics, and boot persistence in one tool
- You need zero-downtime reloads and multi-core cluster workers for a stateless Node network service without deploying an orchestrator
- Your operations workflow benefits from a checked-in ecosystem.config.js that declares processes, environments, memory limits, and restart policy
- You supervise mixed scripts on one machine and want one CLI for names, namespaces, status, logs, stop, restart, and delete
- Your platform already owns process lifecycle through Kubernetes, ECS, Nomad, systemd, or a serverless host; adding PM2 creates a second restart and health-control layer
- Your clustered application keeps sessions, WebSocket ownership, scheduled work, or mutable state in process memory; PM2's cluster guide says apps must be stateless and move shared state elsewhere
- You expect logs to rotate automatically; the core writes files but the official guide requires pm2-logrotate or a separately configured system logrotate job to keep disk use bounded
- You deploy inside a one-process-per-container environment and only need signal forwarding; plain node as PID 1 with an init such as tini is simpler than running a daemon layer
- AGPL-3.0 does not fit your distribution or modification policy; npm identifies PM2 under AGPL-3.0 and the README directs users seeking other terms to contact the vendor
Setup reality
PM2 7 requires Node 18 or newer. A global npm install is the README's easiest path, but it ties operational behavior to whatever version happens to be installed on that host; a local devDependency invoked through npx makes versions reproducible, while the service account and startup script must still find the same Node and PM2 paths. Starting a process is easy. Surviving reboot is two separate steps: pm2 startup prints a privileged command tailored to the detected init system, then pm2 save snapshots the current process list. Forget either step and the app does not return as expected. NVM and other per-user Node installations make the generated PATH and service user important, especially after a Node upgrade. Ecosystem files reduce CLI drift but environment blocks apply only when start or restart uses the matching --env name. Cluster mode is for Node network servers and requires externalized state; reload waits for readiness only when wait_ready is enabled and the app sends ready. PM2 sends termination signals, then kills after kill_timeout, so applications need signal handlers that stop accepting work and close resources. Logs live under the PM2 home directory and do not rotate unless you install pm2-logrotate or configure the operating system. Watch mode can restart stopped apps when files change unless it is disabled while stopping. In containers use pm2-runtime, not the background daemon behavior. Version 7 also dropped Node 16, added Bun support, and removed automatic source-map file detection. The package has many fixed runtime dependencies and runs a resident daemon, so security updates and PM2 upgrades belong in normal host patching rather than being treated as set-and-forget setup.
Patterns
Start and name an applicationstart-named-process
pm2 start dist/server.js --name api --time
pm2 list
pm2 describe apiThe process runs under the current user's PM2 daemon. Use the same service account for later list, logs, save, and startup commands.
Put production settings in an ecosystem filedeclare-ecosystem-file
// ecosystem.config.js
module.exports = {
apps: [{
name: 'api',
script: './dist/server.js',
cwd: '/srv/my-api',
instances: 2,
exec_mode: 'cluster',
max_memory_restart: '750M',
time: true,
}],
};
// shell:
// pm2 start ecosystem.config.jsRelative script and log paths resolve from cwd. Keep this file in version control and avoid putting secrets directly in it.
Start with a named environment blockselect-production-environment
// inside one app declaration
env: { NODE_ENV: 'development', PORT: 3000 },
env_production: { NODE_ENV: 'production', PORT: 8080 },
// shell:
// pm2 start ecosystem.config.js --env productionenv_production is selected only by --env production. Restart with the same --env value when changing environment-specific settings.
Use every available CPU in cluster moderun-cluster-workers
pm2 start dist/server.js --name api -i max
pm2 reload apiCluster mode shares one network port across workers. The application must externalize sessions, WebSocket coordination, jobs, and other mutable state.
Wait for explicit application readinesssignal-readiness
// ecosystem.config.js
module.exports = { apps: [{
name: 'api',
script: './dist/server.js',
wait_ready: true,
listen_timeout: 10000,
}] };
// after the server is actually listening:
server.listen(port, () => process.send?.('ready'));Without wait_ready, PM2 considers the process ready based on its normal startup behavior. Send ready only after required connections and listeners are usable.
Close resources before PM2 kills the processgraceful-shutdown
// ecosystem.config.js: kill_timeout: 10000
process.on('SIGINT', () => {
server.close(async () => {
await database.close();
process.exit(0);
});
});PM2 escalates after kill_timeout. Stop accepting new work immediately and keep the timeout longer than the application's expected drain time.
Back off repeated restartsbackoff-crash-loop
module.exports = { apps: [{
name: 'worker',
script: './dist/worker.js',
min_uptime: '10s',
max_restarts: 10,
exp_backoff_restart_delay: 100,
}] };Backoff reduces pressure during dependency outages. It does not fix a crash loop, so alert on restart count and terminal errored status.
Restart a process that exceeds a memory ceilingrestart-on-memory-limit
pm2 start dist/worker.js --name worker --max-memory-restart 750MMemory-limit checks are periodic rather than instantaneous. Leave headroom for traffic spikes and investigate leaks instead of using restart as the only remedy.
Restart on source changes with exclusionswatch-selected-files
module.exports = { apps: [{
name: 'dev-api',
script: './src/server.js',
watch: ['src'],
watch_delay: 1000,
ignore_watch: ['node_modules', 'logs'],
}] };Watch mode is mainly a development feature. A file change can restart an app after pm2 stop unless watch is disabled with pm2 stop dev-api --watch.
Inspect logs and enable rotationmanage-log-files
pm2 logs api --lines 200
pm2 install pm2-logrotate
pm2 set pm2-logrotate:max_size 50M
pm2 set pm2-logrotate:retain 14Core PM2 log files do not rotate automatically. Installing pm2-logrotate adds another managed PM2 module, so include it in monitoring and upgrades.
Generate startup integration and save processespersist-across-reboot
pm2 startup
# Run the exact privileged command PM2 prints.
pm2 save
pm2 resurrectstartup installs the init integration; save records the current process list. NVM or Node upgrades can invalidate the generated executable path.
Use pm2-runtime as the container commandrun-in-container
FROM node:22-alpine
WORKDIR /app
COPY package*.json ./
RUN npm ci --omit=dev && npm install --global pm2@7.0.3
COPY . .
CMD ["pm2-runtime", "ecosystem.config.js", "--env", "production"]Use pm2-runtime in containers so the foreground process participates in container signals and exit status. Consider plain node when the platform already restarts one process per container.
Alternatives
| Package | Registry | Pick it when |
|---|---|---|
| forever | npm | Use it for basic Node daemonization and restart when PM2's cluster, monitoring, and ecosystem configuration are unnecessary |
| nodemon | npm | Use it only for development-time restart on file changes, not as a production supervisor |
| concurrently | npm | Use it to run several local development commands together when persistence, boot startup, and crash policy are out of scope |