Key takeaways
- PM2 is a process manager for Node.js that restarts crashed apps, captures their logs and restores them after a reboot.
- Apps only come back after a reboot if you ran pm2 startup once and pm2 save after your last change, as the same user that runs the apps.
- pm2 reload gives a zero-downtime restart only in cluster mode; in fork mode it behaves like pm2 restart.
- Install pm2-logrotate on every server, because PM2 never rotates its own log files.
- Changed environment variables are ignored until you restart with --update-env and run pm2 save again.
- When an app keeps restarting, read pm2 logs with --err and the exit code in ~/.pm2/pm2.log before changing anything.
PM2 is a process manager for Node.js: a small background daemon that starts your app, restarts it when it crashes, captures its logs, and brings it back after the server reboots. It is open source, installed from npm, and on a Linux VPS it is the most common way to keep a Node.js or Next.js app running without putting it in a container. This guide covers what PM2 solves, the commands you will use every week, cluster mode, the ecosystem file, log rotation, and how it compares with systemd and Docker.
What problem does PM2 solve?
Run node server.js in an SSH session, close the terminal, and the process dies with the session. Wrap it in nohup or screen and it survives the logout, but nothing restarts it when it throws an unhandled exception at 3 a.m., nothing starts it after the server reboots for a kernel update, and its output goes into a file that grows until the disk is full.
PM2 handles those jobs for you:
- Keeping the process alive. PM2 runs as a daemon under your user and supervises every process it started. When one exits, it starts it again.
- Restarting on your terms. Plain restarts, zero-downtime reloads in cluster mode, a restart when memory crosses a limit, and a backoff delay so a crash loop does not hammer the CPU or the database.
- Logs. The stdout and stderr of each app are written to files under
~/.pm2/logs, andpm2 logstails them. - Startup on boot.
pm2 startupinstalls a boot service (a systemd unit on Ubuntu and Debian) that restores your saved process list when the machine starts.
It also gives you a process table, a live CPU and memory monitor, and cluster mode, which runs several copies of a Node app across CPU cores behind one port.
What PM2 does not do matters just as much. It does not build your app, proxy HTTP, terminate TLS, pull code from Git, or tell anyone when an app crashed. A typical production stack is Nginx in front for the domain and HTTPS, and PM2 behind it keeping node running on a local port. The full setup is in how to deploy a Node.js app to a Linux VPS.
How do you install PM2?
PM2 is an npm package installed globally, so Node.js comes first. On Ubuntu 24.04 or Debian 12, install Node.js 22 LTS from NodeSource or with nvm, because the distribution's own nodejs package is an older major version. Then:
node -v # v22.x
sudo npm install -g pm2@latest
pm2 -v # 5.x or 6.xWith nvm, drop the sudo: global packages live in your home directory.
Install and run PM2 as the user that will own the apps, usually a non-root deploy user rather than root. Every user gets their own PM2 daemon and their own process list under ~/.pm2, and that detail is behind most "my apps vanished" reports later.
To upgrade PM2, install the new version and then run pm2 update, which saves the process list, replaces the running daemon and restores the apps.
What are the core PM2 commands?
Starting an app is one command. You can point PM2 at a script, at an npm script, or at an ecosystem file:
# A plain Node script, with a name you will use in every other command
pm2 start server.js --name api
# An npm script: PM2 supervises npm, which starts your app
pm2 start npm --name web -- start
# Everything defined in an ecosystem file
pm2 start ecosystem.config.js --env productionStarting through npm works, but PM2 then supervises npm rather than your app, shutdown signals pass through an extra process, and cluster mode does not work as intended. In production, point PM2 at the real entry file.
Every other command takes an app name, a numeric id, or all:
| Command | What it does |
|---|---|
pm2 list | The process table: id, name, mode, restart count, status, CPU and memory. pm2 ls and pm2 status are aliases. |
pm2 logs api --lines 100 | Prints the last lines of stdout and stderr, then streams new ones. Omit the name for all apps. |
pm2 restart api | Stops and starts the process. The port is closed for a moment. |
pm2 reload api | Replaces instances one at a time with no downtime in cluster mode; a restart in fork mode. |
pm2 stop api | Stops the process and keeps it in the list. |
pm2 delete api | Stops the process and removes it from the list. |
pm2 describe api | Script path, working directory, log paths, restart counts and more. |
pm2 monit | A live terminal dashboard of CPU, memory and logs per process. |
pm2 save | Writes the current process list to ~/.pm2/dump.pm2. |
pm2 startup | Prints the command that installs the boot service. |
pm2 resurrect | Restores the processes recorded by the last pm2 save. |
pm2 flush api | Empties the app's log files. |
The ↺ column in pm2 list is the restart count. A number that keeps climbing while the status flips between online and errored is the first sign of a crash loop, covered in the troubleshooting section below.
Fork mode vs cluster mode: when does cluster mode help?
Fork mode is the default: PM2 starts one process per app. It works for any script, including non-Node programs started with --interpreter, such as a Python worker.
Cluster mode uses Node's built-in cluster module to start several copies of your app that share one port; the primary process hands incoming connections to the workers. You choose the number of instances:
pm2 start server.js --name api -i max # one instance per CPU core
pm2 start server.js --name api -i 2 # exactly two
pm2 start server.js --name api -i -1 # all cores but oneA single Node process runs your JavaScript on one core. Cluster mode helps when:
- Request handling is CPU-bound, for example server-side rendering, large JSON transforms or image work done in-process, and the server has more than one core.
- You want zero-downtime reloads.
pm2 reloadcan only keep the port open in cluster mode, so even a single instance is often run asexec_mode: 'cluster'withinstances: 1for that reason alone.
It does not help, or actively hurts, when:
- The VPS has one vCPU. There is no second core to use.
- Memory is tight. Each instance is a full copy of the app; four instances of a 300 MB app need about 1.2 GB.
- The app keeps state in memory: sessions, rate-limit counters, caches, WebSocket connections without sticky routing. Each worker has its own copy, so move that state to Redis or a database first.
- The app runs scheduled jobs with
setIntervalor node-cron. They run once per instance. PM2 setsNODE_APP_INSTANCE(0, 1, 2…) in each worker, so run those jobs only where it is0. - The program is not Node. Cluster mode is Node-only.
A sensible default: one or two instances in cluster mode, then add more only after pm2 monit shows the existing ones saturating their cores. The cluster mode documentation covers the details.
How do you write an ecosystem.config.js?
An ecosystem file keeps the whole configuration in one file you can commit, instead of a long command line nobody remembers. pm2 init simple writes a minimal one; here is a fuller example for a Node API:
module.exports = {
apps: [
{
name: 'api',
script: './dist/server.js',
cwd: '/home/deploy/apps/api/current',
exec_mode: 'cluster',
instances: 2,
max_memory_restart: '400M',
env: {
NODE_ENV: 'development',
PORT: 3000,
},
env_production: {
NODE_ENV: 'production',
PORT: 3000,
},
out_file: '/home/deploy/logs/api.out.log',
error_file: '/home/deploy/logs/api.error.log',
merge_logs: true,
time: true,
kill_timeout: 5000,
exp_backoff_restart_delay: 100,
},
],
};Start it with pm2 start ecosystem.config.js --env production, then pm2 save. What each field does:
envalways applies;env_productionis applied on top when you pass--env production. You can addenv_stagingand so on.exec_modeandinstanceschoose fork or cluster mode and the number of workers.max_memory_restartrestarts the app when its memory goes above the value. It accepts K, M and G.out_fileanderror_filemove the logs. The defaults are~/.pm2/logs/<name>-out.logand<name>-error.log.merge_logswrites all cluster workers to one file instead of one file per instance id, andtimeprefixes every line with a timestamp.kill_timeoutis how long PM2 waits after sending SIGINT before it sends SIGKILL. The default is 1.6 seconds; raise it if your app drains connections on shutdown.exp_backoff_restart_delaymakes the gap between crash restarts grow, so a broken app does not restart hundreds of times a minute.
Keep secrets out of this file if it lives in the repository. Read them from a .env file that only exists on the server, or set them in the server's environment. The full list of options is in the application declaration reference, and the free PM2 ecosystem file generator builds a file like the one above from a form.
How do you rotate PM2 logs with pm2-logrotate?
PM2 appends to its log files forever. A chatty app can fill a small VPS disk in weeks, and a full disk takes down everything on the server, not only the noisy app. pm2-logrotate is a PM2 module that runs as its own process and rotates the files PM2 writes, including custom out_file and error_file paths:
pm2 install pm2-logrotate
pm2 set pm2-logrotate:max_size 10M
pm2 set pm2-logrotate:retain 14
pm2 set pm2-logrotate:compress true
pm2 set pm2-logrotate:rotateInterval '0 0 * * *'max_sizerotates a file once it passes this size (default 10M). The size is checked everyworkerIntervalseconds, 30 by default.retainis how many rotated files to keep per log (default 30).compressgzips rotated files (off by default).rotateIntervalis a cron expression for time-based rotation; the default rotates daily at midnight.
The settings persist in ~/.pm2/module_conf.json, and the module shows up as its own row in pm2 list. If you already manage every log with the system's logrotate, a stanza with copytruncate works too. Use one or the other, not both, or files get rotated twice.
What is the difference between pm2 restart and pm2 reload?
pm2 restart stops every instance (SIGINT, then SIGKILL after kill_timeout) and starts them again. Between the two, nothing listens on the port, and Nginx answers requests in that window with 502 Bad Gateway. For a small Express API the gap is usually a fraction of a second; for a Next.js app that takes several seconds to boot, users notice.
pm2 reload in cluster mode replaces instances one at a time: it starts a new worker, waits for it to listen, and only then stops the old one, so some worker is always accepting connections. In fork mode, two processes cannot bind the same port, so reload falls back to a restart.
For a reload that is genuinely zero-downtime:
- Run in cluster mode, even with one instance.
- If the app needs time after listening (warming caches, opening database pools), set
wait_ready: trueand tell PM2 when it is ready. PM2 waits up tolisten_timeoutmilliseconds for the signal. - Handle SIGINT: stop accepting connections, let in-flight requests finish, close database connections, then exit.
const server = app.listen(process.env.PORT, () => {
if (process.send) process.send('ready');
});
process.on('SIGINT', () => {
server.close(() => process.exit(0));
});Deploy updates with pm2 reload ecosystem.config.js --env production and the old code serves traffic until the new code is up.
PM2 vs systemd vs Docker: which should you use?
All three can keep a Node app running. They are not exclusive either: pm2 startup itself creates a systemd unit, and many Docker hosts run systemd. The fair comparison:
| Concern | PM2 | systemd | Docker |
|---|---|---|---|
| Restart on crash | Yes, with backoff and a memory limit | Restart=on-failure with RestartSec | Restart policies such as unless-stopped |
| Start on boot | pm2 startup plus pm2 save | WantedBy=multi-user.target | Docker daemon enabled plus a restart policy |
| Several instances on one port | Built in (cluster mode) | Not built in | Not built in; needs a proxy in front |
| Zero-downtime reload | pm2 reload in cluster mode | Not built in; socket activation can help | Not built in with plain Compose |
| Logs | Files per app; rotation via pm2-logrotate | journald, rotated and size-capped | json-file driver, no rotation unless configured |
| Resource limits | Memory restart threshold only | MemoryMax, CPUQuota via cgroups | --memory, --cpus |
| Isolation | None; same user and filesystem | Optional sandboxing directives | Separate filesystem, process and network namespaces |
| Runs non-Node apps | Yes, fork mode only | Yes | Yes |
| Extra install | npm package | Already on the server | Docker Engine |
Choose PM2 for Node.js apps on a VPS when you want cluster mode, pm2 reload and a familiar CLI with no image builds. It is the shortest path from npm run build to a supervised process.
Choose systemd when you want nothing extra on the server, strict memory and CPU limits, sandboxing, or one way to manage every service whether it is Node or not. A unit for the same API looks like this, and the systemd service generator writes one for you:
[Unit]
Description=api
After=network.target
[Service]
User=deploy
WorkingDirectory=/home/deploy/apps/api/current
ExecStart=/usr/bin/node dist/server.js
Environment=NODE_ENV=production PORT=3000
Restart=on-failure
RestartSec=2
MemoryMax=512M
[Install]
WantedBy=multi-user.targetChoose Docker when the app needs system libraries you do not want on the host, when the stack has several services (app, database, queue), or when the same image must run identically on a laptop and a server. Set log rotation on the json-file driver, or use the local driver, which rotates by default. See deploying a Docker Compose app to a VPS for that route.
How do you run Next.js under PM2?
There are two common ways, and both start with npm ci and npm run build. next start refuses to run without a production build in .next.
The first runs next start through the Next.js binary directly, so cluster mode works:
module.exports = {
apps: [
{
name: 'web',
cwd: '/home/deploy/apps/web/current',
script: 'node_modules/next/dist/bin/next',
args: 'start -p 3000 -H 127.0.0.1',
exec_mode: 'cluster',
instances: 2,
max_memory_restart: '600M',
env_production: {
NODE_ENV: 'production',
},
},
],
};The second uses output: 'standalone' in next.config. The build then produces .next/standalone/server.js, a minimal server with only the dependencies it needs. Next.js does not copy public and .next/static into it, so do that yourself, then start the server with PORT and HOSTNAME set:
cp -r public .next/standalone/
cp -r .next/static .next/standalone/.next/
PORT=3000 HOSTNAME=127.0.0.1 pm2 start .next/standalone/server.js --name webThe Next.js output option docs cover standalone mode in full. Either way:
- Bind to 127.0.0.1 so the app is reachable only through Nginx.
- Do not rebuild inside the directory the running app serves from. A half-written
.nextfolder serves errors until the build finishes. Build into a new release directory, then point PM2 at it. NEXT_PUBLIC_variables are inlined at build time. Changing one needs a rebuild, not just a restart.
The whole route, with Nginx and HTTPS, is in how to deploy a Next.js app to a Linux VPS.
Common PM2 problems and how to fix them
The app keeps restarting
The restart count climbs, and the status flips between online and errored. After a run of rapid crashes (set by max_restarts, counted against min_uptime), PM2 stops trying and leaves the app errored. Read before you change anything:
pm2 describe api # restarts, unstable restarts, log paths
pm2 logs api --err --lines 100 --nostream # the last errors, without streaming
grep 'api' ~/.pm2/pm2.log | tail -n 20 # exit codes and signalsThe daemon log has lines like App [api:0] exited with code [1] via signal [SIGINT]. How to read them:
- Code 1 with a stack trace in the error log: the app threw at startup. A missing environment variable, an unreachable database or a bad config path are the usual causes.
- EADDRINUSE: something else holds the port.
sudo ss -ltnp | grep :3000shows what. Often it is an old copy started by hand, a second app with the samePORT, or the same app under another user's PM2 daemon. - SIGKILL with no stack trace: something killed it from outside, usually the kernel's out-of-memory killer.
journalctl -k | grep -i 'killed process'confirms it. - Code 0 again and again: the script finishes on its own. PM2 expects a long-running process; a one-shot task belongs in cron or needs
autorestart: false.
Apps do not come back after a reboot
pm2 startup only installs the boot service. What it restores on boot is whatever pm2 save last wrote, as the user the service was set up for.
pm2 startup systemd
# PM2 prints a command like this one; copy and run it exactly:
# sudo env PATH=$PATH:/usr/bin /usr/lib/node_modules/pm2/bin/pm2 startup systemd -u deploy --hp /home/deploy
pm2 save
systemctl status pm2-deploy --no-pagerThen reboot once and check pm2 list. If the list is empty after a reboot:
pm2 savewas never run, or not after the last app was added. Run it after every change to the process list.- The service belongs to a different user. Setting up startup as root while the apps run as
deployrestores root's empty list. The unit is namedpm2-<user>. - Node moved. After a Node upgrade, especially with nvm, the unit still points at the old binary. Run
pm2 unstartup systemd, thenpm2 startupagain.
Memory keeps growing
Watch the memory column in pm2 list or pm2 monit. Memory that climbs steadily and never levels off is a leak. max_memory_restart is the safety net: PM2 checks memory periodically and restarts the app once it is over the limit. That keeps the server alive, but it does not fix anything; take heap snapshots with node --inspect and Chrome DevTools to find the cause.
If the error log shows JavaScript heap out of memory, Node hit its own heap limit first. Set it explicitly with node_args: '--max-old-space-size=384' in the ecosystem file, keep max_memory_restart above that, and on a 1 GB VPS add swap so a spike during a build does not end in the OOM killer.
Environment variable changes are ignored
PM2 stores an app's environment when it first starts it, and a plain pm2 restart reuses the stored copy. To pick up new values:
pm2 restart api --update-env
# or, from the ecosystem file
pm2 reload ecosystem.config.js --env production --update-env
pm2 saveRun pm2 save afterwards, because the dump file records the environment too; without it, a reboot brings the old values back. Values your app reads itself from a .env file at startup are picked up by any restart, since the file is read again. pm2 env 0 shows what process 0 actually received.
pm2 list is empty but the app is running
You are looking at the wrong daemon. pm2 list as deploy and sudo pm2 list show two separate process lists. Find which user owns the process with ps -o user= -p <pid> and manage it as that user.
How does Shipown use PM2?
Shipown is a desktop app for Windows, macOS and Linux that deploys to your own Linux server over SSH, with no agent installed on the server. For Node applications, PM2 is the runtime: Next.js, Nuxt, Remix, SvelteKit, NestJS, Fastify and plain Express apps are installed, built and started on your server under PM2, with the port, the health check and the restart policy set for you. When you register a server, setup offers PM2 preparation if it is missing.
Each deploy lands in its own release directory, and the live pointer moves only after the build and the health check pass. A failed deploy restores the previous release by itself, and rolling back later is choosing an earlier release, as described on the rollback feature page and in the deployments docs. You can build on the server or on your PC.
After the deploy, the app reads PM2 state live. PM2 processes appear beside CPU, memory and disk on the server page, and the Overview feed re-reads PM2 apps every fifteen seconds and calls out an app going down, coming back or restarting, as described in live updates. pm2-logrotate can be installed and configured from the app, and an app's logs flushed, alongside cron, firewall and swap tools covered on the server management page and in the maintenance docs.
What stays manual: a person presses deploy, since Shipown does not deploy on git push. The monitoring and alerts run only while the desktop app is running, even minimized to the tray, and the PC is awake. PM2 on the server keeps restarting a crashed app either way, which is PM2 doing its job. Your app still has to handle SIGINT and keep its memory in check; no tool fixes a leak for you.
What should you set up once PM2 is running?
- Run
pm2 startupandpm2 save, then reboot once to prove the apps come back. - Install pm2-logrotate with a size limit and a retention count.
- Set
max_memory_restarton every app and add swap on small servers. - Put Nginx in front, bind the app to 127.0.0.1, and close the app port in the firewall.
- Ship updates into a new release directory and
pm2 reload, keeping the previous build so you can switch back; rolling back a deployment on a VPS shows how. - Add monitoring from outside the server. PM2 restarts a crashed app silently, and a restart count of 300 is something you want to hear about before your users do.
Frequently asked questions
What is PM2 used for?
PM2 is a process manager for Node.js applications on servers. It keeps an app running in the background, restarts it when it crashes, captures its logs to files, runs several instances in cluster mode, and restores the apps after a reboot once pm2 startup and pm2 save have been run.
Is PM2 free?
Yes. The PM2 command-line tool is open source and free to use in production, installed with npm install -g pm2. Everything in this guide, including cluster mode, the ecosystem file and pm2-logrotate, works without an account.
Should I use PM2 or systemd for a Node.js app?
Use PM2 if you want cluster mode, zero-downtime reloads with pm2 reload and a CLI built for Node. Use systemd if you want nothing extra installed, hard memory and CPU limits, sandboxing and one tool for every service. Both restart crashed apps and start them on boot.
Why does my PM2 app not start after a reboot?
Usually pm2 save was not run after the last change, or pm2 startup was set up for a different user than the one running the apps. Run pm2 startup as the app user, run the sudo command it prints, start your apps, run pm2 save, and test with one reboot.
What is the difference between pm2 restart and pm2 reload?
pm2 restart stops all instances and starts them again, so the port is closed briefly. pm2 reload replaces cluster-mode instances one at a time and only stops an old one after its replacement is listening, so there is no downtime. In fork mode, reload behaves like restart.
Do I need PM2 if I use Docker?
Usually not. Docker restart policies already restart a crashed container and start it on boot, so running PM2 inside a container adds a second supervisor. The exception is pm2-runtime, PM2's container entrypoint, which some teams use to get cluster mode inside one container.
How do I see why a PM2 app crashed?
Run pm2 logs with the app name, --err and --lines 100 to read the last errors, and check ~/.pm2/pm2.log for the exit code and signal. An exit code of 1 with a stack trace means the app threw; SIGKILL with no stack trace usually means the kernel killed it for using too much memory.
