
- PM2
- Node.js
- Deployment
- Process Management
- VPS
PM2 Complete Guide — Deploy & Manage Node.js Apps in Production
Everything you need to run Node.js apps with PM2 — start, stop, monitor, cluster mode, log management, ecosystem.config.js, startup scripts, and zero-downtime reloads.
PM2 is the standard process manager for Node.js applications in production. It keeps your app running after crashes, restarts it on server reboots, provides built-in log management, and supports cluster mode for multi-core performance. This guide covers everything you need for a production setup.
Installation
Install PM2 globally with npm:
sudo npm install -g pm2
Verify the installation:
pm2 --version
Starting Applications
Start a script directly
pm2 start app.js --name "my-app"
Start an npm script
pm2 start npm --name "my-app" -- start
Start a specific entry point
For Astro (Node adapter), Next.js standalone, or any custom server file:
# Astro (Node adapter)
pm2 start dist/server/entry.mjs --name "astro-app"
# Next.js standalone
pm2 start .next/standalone/server.js --name "next-app"
# Custom Express server
pm2 start server.js --name "api-server"
For a complete Astro + Nginx + PM2 setup, see Deploying Astro on a VPS with Nginx. For Next.js deployments, see Deploying Next.js with Nginx or Deploying Next.js with Apache2.
Pass environment variables at start
pm2 start app.js --name "my-app" --env production
# or explicitly:
PORT=8080 NODE_ENV=production pm2 start app.js --name "my-app"
Core Process Commands
pm2 list # List all running processes
pm2 status # Same as pm2 list
pm2 show my-app # Detailed info for one process
pm2 stop my-app # Stop (keeps in list)
pm2 start my-app # Start a stopped process
pm2 restart my-app # Restart (brief downtime)
pm2 reload my-app # Zero-downtime restart (cluster mode)
pm2 delete my-app # Remove from list entirely
pm2 stop all # Stop all processes
pm2 restart all # Restart all processes
pm2 delete all # Remove all processes
Processes can be referenced by name or by their numeric ID shown in pm2 list.
To remove a process from PM2 entirely (stop + delete from the list), use pm2 delete:
pm2 delete my-app # remove one process by name or ID
pm2 delete all # remove all processes
After deleting, run pm2 save to update the saved list so the process doesn’t come back after a reboot.
Surviving Server Reboots
This is the most important step for production. Without it, your app won’t restart after a reboot.
1. Generate a startup script
pm2 startup
PM2 prints a command tailored to your system. Copy and run it — it looks like:
sudo env PATH=$PATH:/usr/bin /usr/lib/node_modules/pm2/bin/pm2 startup systemd -u ubuntu --hp /home/ubuntu
2. Save the current process list
pm2 save
This saves the list of running processes to ~/.pm2/dump.pm2. On reboot, PM2 restores them automatically.
Run pm2 save again whenever you add, remove, or change a process.
3. Restore a saved process list
If you need to manually load the saved list back — for example after a pm2 kill or on a fresh login — run:
pm2 resurrect
This reloads all processes from ~/.pm2/dump.pm2, starting them exactly as they were when you last ran pm2 save.
4. Verify startup is registered
pm2 ls # confirm processes are listed
sudo systemctl status pm2-$(whoami)
Ecosystem Configuration File
For anything beyond a simple one-liner, use an ecosystem file. It keeps all your PM2 configuration in version control.
Create ecosystem.config.cjs in your project root (next to package.json). PM2 also stores its internal state — the saved process list and logs — under ~/.pm2/:
~/.pm2/dump.pm2— saved process list (pm2 save/pm2 resurrect)~/.pm2/logs/— per-process log files~/.pm2/pm2.log— PM2 daemon log
// ecosystem.config.cjs
module.exports = {
apps: [
{
name: 'my-app',
script: 'dist/server/entry.mjs',
// Number of instances — 'max' uses all CPU cores
instances: 1,
// Restart automatically on crash
autorestart: true,
// Watch files and restart on changes (use in dev only)
watch: false,
// Memory limit — restart if exceeded
max_memory_restart: '512M',
// Environment variables per mode
env: {
NODE_ENV: 'development',
PORT: 4321,
},
env_production: {
NODE_ENV: 'production',
PORT: 4321,
},
},
],
};
Start with the ecosystem file:
# Development environment
pm2 start ecosystem.config.cjs
# Production environment
pm2 start ecosystem.config.cjs --env production
Other commands work the same way:
pm2 restart ecosystem.config.cjs --env production
pm2 reload ecosystem.config.cjs --env production # zero-downtime
pm2 stop ecosystem.config.cjs
pm2 delete ecosystem.config.cjs
Cluster Mode
Cluster mode spawns one process per CPU core and load-balances incoming connections across them. This is the fastest way to use all cores on a multi-core VPS without changing your app code.
// ecosystem.config.cjs
module.exports = {
apps: [
{
name: 'my-app',
script: 'server.js',
instances: 'max', // one per CPU core
exec_mode: 'cluster', // required for load balancing
autorestart: true,
env_production: {
NODE_ENV: 'production',
PORT: 3000,
},
},
],
};
In cluster mode, use pm2 reload instead of pm2 restart to achieve zero-downtime updates — PM2 cycles workers one at a time.
Caution: Cluster mode requires your app to be stateless. In-memory session storage, WebSocket connections, and anything stored in a global variable will not be shared across workers. Use Redis or a database for shared state.
Log Management
View logs
pm2 logs # Stream all logs
pm2 logs my-app # Stream logs for one app
pm2 logs my-app --lines 100 # Show last 100 lines
Log file locations
Logs are stored in ~/.pm2/logs/:
~/.pm2/logs/my-app-out.log # stdout
~/.pm2/logs/my-app-error.log # stderr
Flush logs
pm2 flush # Delete all log files
pm2 flush my-app # Delete logs for one app
Log rotation
Install the PM2 log rotation module to avoid logs filling up the disk:
pm2 install pm2-logrotate
Configure it:
pm2 set pm2-logrotate:max_size 50M # rotate when file exceeds 50 MB
pm2 set pm2-logrotate:retain 14 # keep 14 rotated files
pm2 set pm2-logrotate:compress true # gzip rotated files
pm2 set pm2-logrotate:dateFormat YYYY-MM-DD_HH-mm-ss
Monitoring
Built-in terminal dashboard
pm2 monit
Shows CPU and memory usage per process in real time.
Process metrics
pm2 show my-app
Displays uptime, restart count, memory, CPU, log paths, and more.
Web dashboard (PM2 Plus)
PM2 offers a hosted web dashboard at app.pm2.io. Free tier available. Connect with:
pm2 link <secret_key> <public_key>
Deployment Workflow
A typical deployment after a code update:
# 1. Pull latest code on server
git pull
# 2. Install any new dependencies
npm install --omit=dev
# 3. Rebuild (if applicable)
npm run build
# 4. Reload the process (zero-downtime in cluster mode, brief restart otherwise)
pm2 reload my-app
# 5. Save the process list if anything changed
pm2 save
Watch Mode (Development Only)
PM2 can watch files and restart the process automatically on changes. Never use this in production — it restarts on every file modification and causes excessive CPU usage.
pm2 start app.js --name "dev-app" --watch
pm2 start app.js --name "dev-app" --watch --ignore-watch="node_modules logs"
Important Notes
- Always run
pm2 saveafter adding or changing processes, so the state persists across reboots. - In cluster mode, use
pm2 reloadnotpm2 restartto achieve zero-downtime updates. - Avoid
--watchin production — it is for development only. - Store your
ecosystem.config.cjsin version control so your PM2 setup is reproducible. - Use
pm2-logrotateon any long-running server to prevent logs from filling your disk.
Frequently Asked Questions
What’s the difference between pm2 restart and pm2 reload?
restart kills the process and starts a new one — there is a brief moment of downtime. reload (cluster mode only) replaces workers one at a time while the others continue handling requests, achieving zero downtime.
My app isn’t restarting after a server reboot. What’s wrong?
You likely skipped one of the two steps: running the pm2 startup command (including the sudo env ... command it prints) and running pm2 save. Both are required.
How do I run multiple apps on the same server?
List them all in the apps array of your ecosystem file, or start each one separately with a unique --name. Give each app a different port and create a separate Nginx server block for each domain.
How do I set environment variables securely?
Don’t store secrets in ecosystem.config.cjs if it’s in version control. Instead, set them in a .env file on the server and load them at startup, or export them in the shell before running PM2. Alternatively use the env_production block in the ecosystem file for non-sensitive config only.
Can PM2 manage non-Node.js processes?
Yes — PM2 can manage any executable. Use interpreter: 'python3' or interpreter: 'none' in the ecosystem file. However, PM2 was designed for Node.js and its language-specific features (cluster mode, --harmony flags, etc.) only apply to Node.js processes.
How do I completely uninstall PM2?
pm2 delete all
pm2 unstartup
sudo npm uninstall -g pm2
rm -rf ~/.pm2