| name | pm2 |
| description | Process management with PM2 — start, stop, restart, monitor long-running processes. Use when: keeping services alive, auto-restart on crash, managing daemon processes, ecosystem configs, log management, startup scripts, process monitoring. Triggers: pm2, process manager, keep alive, daemon, auto-restart, ecosystem config, process monitoring. |
PM2 — Process Manager
PM2 keeps processes alive, restarts them on crash, and provides monitoring/logging.
Use for long-running services, persistent agents, background workers.
Not for detached terminals — use holdpty when you need PTY output, attach/view, or interactive sessions.
Not for ephemeral tasks — use pi -p > file & for quick fire-and-forget agent runs.
Quick Reference
Start a process
pm2 start server.js --name myapp
pm2 start script.py --interpreter python3 --name worker
pm2 start app.js --name api -- --port 3000 --env production
pm2 start ecosystem.config.cjs
Manage processes
pm2 list
pm2 jlist
pm2 info <name|id>
pm2 restart <name|id|all>
pm2 stop <name|id|all>
pm2 delete <name|id|all>
pm2 restart <name> --update-env
Logs
pm2 logs
pm2 logs <name> --lines 50
pm2 flush
Log files location: ~/.pm2/logs/<name>-out.log and <name>-error.log.
Monitoring
pm2 monit
pm2 dash
Ecosystem Config
For reproducible multi-process setups, use an ecosystem.config.cjs file:
module.exports = {
apps: [
{
name: "api",
script: "dist/server.js",
instances: 2,
exec_mode: "cluster",
env: {
NODE_ENV: "production",
PORT: 3000,
},
max_memory_restart: "500M",
log_date_format: "YYYY-MM-DD HH:mm:ss",
},
{
name: "worker",
script: "dist/worker.js",
autorestart: true,
max_restarts: 10,
restart_delay: 5000,
exp_backoff_restart_delay: 100,
watch: false,
},
],
};
pm2 start ecosystem.config.cjs
pm2 start ecosystem.config.cjs --only api
pm2 restart ecosystem.config.cjs
pm2 delete ecosystem.config.cjs
Useful ecosystem options
| Option | Type | Description |
|---|
script | string | Script to run (required) |
interpreter | string | Override interpreter (default: node) |
args | string or string[] | Script arguments |
cwd | string | Working directory |
instances | number | Number of instances (cluster mode) |
exec_mode | string | "fork" (default) or "cluster" |
autorestart | boolean | Auto-restart on exit (default: true) |
max_restarts | number | Max consecutive restarts before stopping |
restart_delay | number | Delay between restarts (ms) |
exp_backoff_restart_delay | number | Exponential backoff base (ms) |
max_memory_restart | string | Restart if memory exceeds (e.g. "500M") |
cron_restart | string | Cron-based restart schedule |
watch | boolean or string[] | Watch for file changes |
ignore_watch | string[] | Paths to ignore when watching |
env | object | Environment variables |
log_date_format | string | Timestamp format for logs |
error_file | string | Custom stderr log path |
out_file | string | Custom stdout log path |
merge_logs | boolean | Merge cluster instance logs |
stop_exit_codes | number[] | Exit codes that skip auto-restart |
Persistence
pm2 save
pm2 resurrect
pm2 startup
pm2 unstartup
After pm2 startup, run the command it outputs (may need admin/sudo).
Then pm2 save to snapshot current processes — they'll auto-start on reboot.
Windows Gotchas
.cmd wrapper resolution
PM2 tries to run .cmd files as Node.js scripts. Never start a .cmd shim directly with PM2.
pm2 start pi -- -p "prompt"
pm2 start /path/to/cli.js --interpreter node -- -p "prompt"
For npm-installed CLIs, find the real script:
cat "$(which pi)" | head -5
In ecosystem configs, always use the resolved .js path:
module.exports = {
apps: [{
name: "my-agent",
script: "C:\\path\\to\\node_modules\\package\\dist\\cli.js",
interpreter: "node",
args: ["--mode", "json"],
}],
};
Log paths
PM2 stores logs at ~/.pm2/logs/. On Windows this is typically C:\Users\<user>\.pm2\logs\.
Daemon
PM2 daemon runs as a background Node.js process. pm2 kill stops the daemon and all managed processes. pm2 ping checks if the daemon is running.
Agent Patterns
Launch a pi agent as a persistent service
First, find the actual cli.js path (see Windows Gotchas above):
cat "$(which pi)" | head -5
module.exports = {
apps: [{
name: "my-agent",
script: "/path/to/node_modules/@mariozechner/pi-coding-agent/dist/cli.js",
interpreter: "node",
args: ["--mode", "json", "--cwd", "/path/to/project"],
autorestart: true,
max_restarts: 10,
restart_delay: 5000,
}],
};
Note: pi -p to non-TTY only outputs final text. Use --mode json for full event streaming to PM2 logs.
Check process health from an agent
pm2 jlist | node -e "
const d = JSON.parse(require('fs').readFileSync('/dev/stdin','utf8'));
d.forEach(p => console.log(p.name, p.pm2_env.status, 'restarts:', p.pm2_env.restart_time));
"
Rotate logs
pm2 install pm2-logrotate
pm2 set pm2-logrotate:max_size 10M
pm2 set pm2-logrotate:retain 5
When NOT to Use PM2
- Detached terminal sessions → use holdpty (PTY output, attach/view)
- Ephemeral agent runs → use
pi -p > file & (fire-and-forget with output capture)
- Containers → the container runtime manages lifecycle; PM2 inside Docker is usually redundant
- Systemd environments → use systemd service units natively on Linux