How to Use PM2 to Manage Node.js Apps on a VPS
CodexFuse Team · 2026-11-06T08:19:52.600Z · 23 min read min read
How to Use PM2 to Manage Node.js Apps on a VPS
Learning how to use pm2 is the step that turns a Node.js app you started by hand into a service that stays online. PM2 is a production process manager for Node.js apps: it starts your app in the background, restarts it when it crashes, and brings it back after the server reboots. Without it, your app dies the moment you close your terminal, and a 3 a.m. crash stays dead until you wake up and type the command again.
This guide walks the whole path on a virtual private server: install PM2, start your first app, give it a name, make it survive reboots, scale it with cluster mode, and read its logs when something goes wrong. Every command below was checked against the official PM2 docs and the current release, PM2 7.1.2, published on 3 November 2026. If you do not have a server yet, our VPS hosting guide explains what a VPS is and how to pick one before you follow the steps here.
What Is PM2?
PM2 is a production process manager for Node.js and Bun applications with a built-in load balancer. It keeps applications alive forever, reloads them without downtime, and handles common system administration tasks. That is the definition from the PM2 project itself, and it is the whole job in one sentence: PM2 watches your app and keeps it running.
A process manager is not a web server. PM2 keeps your Node.js app alive and balances traffic between its instances, but public HTTP and HTTPS traffic still needs a reverse proxy such as Nginx in front of it, and that boundary matters: PM2 never replaces Nginx. What PM2 does replace is the fragile habit of running node app.js in a terminal and hoping nothing goes wrong.
PM2 is free and open source under the GNU Affero General Public License 3.0, and the project is actively maintained: it has more than 43,000 stars on GitHub, it was created in May 2013, 4,917 days before this guide was written, and its latest release, 7.1.2, shipped on 3 November 2026, three days before this guide was written. It runs on Linux, macOS, and Windows, and version 7 needs Node.js 18 or newer. It also manages scripts in other languages, including Python, Bash, and compiled binaries, but this guide stays on Node.js, which is what nearly every VPS user runs under it.
How Do You Install PM2 on a VPS?
Install Node.js first, then install PM2 globally with npm. PM2 is an npm package, so it needs Node.js and npm on the server before anything else works. Use a current LTS release of Node.js: as of November 2026 the current LTS lines are Node 22 (Jod), Node 24 (Krypton), and Node 26 (Lithium), and production apps should only use an Active or Maintenance LTS release.
On an Ubuntu server, install Node.js from the official NodeSource repository or with nvm, then run the install command. Our Ubuntu beginner guide covers the server basics if Ubuntu is new to you. The PM2 install itself is one command:
npm install pm2@latest -g
The -g flag installs PM2 globally, so the pm2 command works from any directory. If npm refuses with a permission error, do not install it with sudo and a plain npm command as a first resort: the usual fix is to install Node.js through nvm under your own user, which keeps the global install in your home directory.
Once the install finishes, confirm it with pm2 -v, which should print 7.1.2. Updating later uses the same install command followed by pm2 update, which updates the running PM2 daemon and restores your processes.
How Do You Start Your First App with PM2?
Starting an app with PM2 takes five steps, and the whole flow fits in one terminal session. Run these in your app directory, and your app will be running in the background, named, and set to come back after a reboot.
- Install Node.js and PM2 on the VPS with
npm install pm2@latest -g. - Start the app with a name:
pm2 start app.js --name my-app. - Check that it is online:
pm2 list. - Register it with the boot system: run
pm2 startup, then run the command it prints. - Save the process list so it is restored on boot:
pm2 save.
The pm2 start command is the heart of the tool, and it does more than launch a file. It daemonizes the app, which means the app keeps running after you close the terminal, and it gives the app an id and a name you can use in every later command. Always pass a name with --name: without one, PM2 uses the script filename, and two apps called app.js in different folders become hard to tell apart in the process list.
You can start more than a Node.js file. PM2 also starts a Bash script with pm2 start bashscript.sh, a Python app with pm2 start python-app.py --watch, and a binary with arguments after a double dash, such as pm2 start binary-file -- --port 1520.
For an npm script, start npm itself: pm2 start npm --name my-app -- start, where the double dash passes start through to npm. Check the result with pm2 list, which shows each app's name, id, status, restart count, CPU use, and memory use in one table.
What Does Each PM2 Command Do?
Everyday PM2 work uses a small set of commands, and the differences between two of them cause most beginner mistakes. The table below is the set you will actually type, with what each one does to a running app.
| Command | What it does |
|---|---|
pm2 start app.js | Starts and daemonizes the app in the background. |
pm2 start app.js --name my-app | Starts the app under a readable name you choose. |
pm2 list | Lists every managed app with status, CPU, and memory. |
pm2 restart my-app | Kills the app and starts it again, with a short downtime. |
pm2 reload my-app | Reloads the app with zero downtime for networked apps. |
pm2 stop my-app | Stops the app but keeps it in the process list. |
pm2 delete my-app | Stops the app and removes it from the process list. |
pm2 logs my-app | Streams the app logs in real time. |
pm2 monit | Opens a live terminal dashboard of CPU, memory, and logs. |
pm2 scale my-app 2 | Changes a cluster-mode app to two running instances. |
pm2 describe 0 | Shows full details for one process by id. |
pm2 save | Freezes the process list so it is restored after a reboot. |
The pair to learn carefully is restart versus reload. pm2 restart kills the process and starts it again, so there is a brief gap where the app answers nothing. pm2 reload starts a new process and hands traffic over before stopping the old one, which gives zero downtime, but only for networked apps.
For a worker or a script that does not serve requests, a reload behaves like a restart. Use reload for deploys of a web app and restart for everything else, and use the app name or id, or all, as the target in either command.
How Does the PM2 Ecosystem File Work?
A pm2 ecosystem file is a single configuration file that declares every app PM2 should manage, with its name, script, environment, and restart rules in one place. It replaces a growing pile of long command lines with a file you can keep in version control, and it is the step that turns a one-app server into a repeatable setup.
Generate a starter file with pm2 ecosystem or pm2 init simple, both of which create ecosystem.config.js. Ecosystem files also come in YAML and JSON forms, but the JavaScript form is the documented default. A typical file for one API app and one worker looks like this:
module.exports = {
apps: [{
name: "api",
script: "./app.js",
instances: "max",
exec_mode: "cluster",
max_memory_restart: "500M",
env: { NODE_ENV: "development" },
env_production: { NODE_ENV: "production" }
}, {
name: "worker",
script: "worker.js",
cron_restart: "0 0 * * *"
}]
};
// Start it with: pm2 start ecosystem.config.js --env production
// One app only: pm2 start ecosystem.config.js --only api
Start everything in the file with pm2 start ecosystem.config.js, act on one app with pm2 start ecosystem.config.js --only api, and switch environments with --env production, which picks up the env_production block. The same file form works for stop, restart, reload, and delete, so a deploy becomes one command instead of five. The fields worth knowing first are in the table below.
| Field | What it controls |
|---|---|
name | The app name shown in the process list. |
script | The file or command PM2 runs. |
instances | How many processes to run; "max" means one per CPU. |
exec_mode | "cluster" or "fork"; cluster shares ports across instances. |
env and env_production | Environment variables, switched with --env. |
max_memory_restart | Restarts the app when it passes a memory limit, such as "500M". |
cron_restart | Restarts the app on a cron schedule, such as "0 0 * * *" for midnight. |
watch | Restarts the app when files change; a development convenience. |
max_restarts | Consecutive unstable restarts allowed before the app is marked errored; default 16. |
min_uptime | How long a process must stay up to count as stable; default 1,000 ms. |
kill_timeout | How long PM2 waits for a graceful shutdown before forcing it; default 1,600 ms. |
error_file and out_file | Where error and standard output logs are written. |
autorestart | Whether PM2 restarts a crashed app; default true. |
Two of these fields deserve a warning in plain words. Watch mode restarts your app whenever a file changes, which is handy while you develop, but it is a development convenience: do not switch it on in production, where a deploy or a stray file write would restart your live app without you asking. And max_memory_restart is a safety net for a slow memory leak, not a fix for one: if your app restarts every time it passes 500 MB, find the leak instead of raising the limit forever.
How Do You Make PM2 Restart on Reboot?
To make pm2 restart on reboot work, you need two separate steps, and skipping either one is the most common reason an app does not come back after a server restart. The first step registers PM2 itself with the boot system, and the second step saves the list of apps it should bring back.
Run pm2 startup first. It detects the init system on your server, which is systemd on Ubuntu 16 and newer, CentOS 7 and newer, Debian 7 and newer, and Arch, and it prints a second command for you to copy and run.
That generated command needs sudo or root on a VPS, and it looks like sudo env PATH=$PATH:/home/youruser/.nvm/versions/node/v22.0.0/bin pm2 startup systemd -u youruser --hp /home/youruser. Run the first pm2 startup without sudo, then run the printed command with sudo.
Then run pm2 save. Saving freezes the current process list to disk, and that saved list is what PM2 resurrects when the server boots.
Startup alone does not persist the app list, and saving alone does not register PM2 with the boot system: both are needed for reboot survival. To restore a saved list by hand, run pm2 resurrect, and to remove the boot registration, run pm2 unstartup.
One detail catches people after a Node.js upgrade. The generated startup command embeds the current Node binary path, so if you installed Node through nvm and later switch versions, the startup unit still points at the old Node.
After a Node upgrade, run pm2 unstartup and then pm2 startup again so the unit points at the new binary. You can check the unit itself with systemctl cat pm2-youruser and read its boot log with journalctl -u pm2-youruser.
What Is PM2 Cluster Mode?
PM2 cluster mode runs several copies of your app as separate processes and balances incoming requests between them, using the Node.js cluster module so the copies share one server port. It needs no code changes for a networked Node.js app, and it is how a single app uses more than one CPU core, because one Node.js process runs on one thread by default.
Start an app in cluster mode with pm2 start app.js -i max. The -i max flag, or 0, tells PM2 to detect the number of available CPUs and run one process per CPU.
A number runs exactly that many instances, and -1 runs all CPUs minus one. On a 1-vCPU VPS, -i max starts a single process, so do not promise yourself multi-process scaling on a small droplet: cluster mode only multiplies what the hardware has. The numbers work out like this for an app that uses about 256 MB per process:
| VPS size | Instances with -i max | Memory at 256 MB each |
|---|---|---|
| 1 vCPU | 1 | 256 MB |
| 2 vCPU | 2 | 512 MB |
| 4 vCPU | 4 | 1,024 MB |
| 8 vCPU | 8 | 2,048 MB |
The same numbers, plus the log volumes from the next section and the age of the project, come from a short script that anyone can run and check. The script below was run on 6 November 2026, and its output is printed underneath it.
from datetime import date
today = date(2026, 11, 6)
print("PM2 cluster math (256 MB per app process):")
for vcpu in (1, 2, 4, 8):
print(f" {vcpu} vCPU -> {vcpu} instance(s), {vcpu * 256} MB total")
print("Log volume at 1 MB per hour per process:")
for procs in (1, 2, 4):
print(f" {procs} process(es) -> {procs * 24} MB/day, {procs * 24 * 7} MB/week")
print("Days since PM2 7.1.2 (2026-11-03):", (today - date(2026, 11, 3)).days)
print("Days since PM2 created (2013-05-21):", (today - date(2013, 5, 21)).days)
print("Years since PM2 created:", round((today - date(2013, 5, 21)).days / 365.25, 1))
print("Default max_restarts:", 16, "| min_uptime:", 1000, "ms | kill_timeout:", 1600, "ms")
print("Log lines shown by pm2 logs by default:", 15)
print("Node floor for PM2 7.x:", 18)
# Expected output:
# PM2 cluster math (256 MB per app process):
# 1 vCPU -> 1 instance(s), 256 MB total
# 2 vCPU -> 2 instance(s), 512 MB total
# 4 vCPU -> 4 instance(s), 1024 MB total
# 8 vCPU -> 8 instance(s), 2048 MB total
# Log volume at 1 MB per hour per process:
# 1 process(es) -> 24 MB/day, 168 MB/week
# 2 process(es) -> 48 MB/day, 336 MB/week
# 4 process(es) -> 96 MB/day, 672 MB/week
# Days since PM2 7.1.2 (2026-11-03): 3
# Days since PM2 created (2013-05-21): 4917
# Years since PM2 created: 13.5
# Default max_restarts: 16 | min_uptime: 1000 ms | kill_timeout: 1600 ms
# Log lines shown by pm2 logs by default: 15
# Node floor for PM2 7.x: 18
Cluster mode has one design rule that matters more than the command. Apps in cluster mode should be stateless: keep no sessions or per-process data in memory, because a returning visitor may land on a different process next time.
Use a shared store such as Redis or MongoDB for sessions and shared state, and catch the SIGINT signal in your app so it can finish queries and close database connections before PM2 shuts it down. In an ecosystem file, cluster mode is instances: "max" plus exec_mode: "cluster", and you can grow or shrink a running cluster with pm2 scale my-app 2.
How Do You Read PM2 Logs?
PM2 logs are the first place to look when an app misbehaves, and the commands are short. pm2 logs streams the logs of every managed app in real time, pm2 logs my-app follows one app, and pm2 logs --lines 200 shows the last 200 lines instead of the default 15. Add --err or --out to see only errors or only standard output, --json for machine-readable lines, and --timestamp to prefix each line with its time.
By default, logs live under $HOME/.pm2/logs, in files named after the app: my-app-error.log and my-app-out.log, with the process id appended unless merge_logs is set in the ecosystem file. You can redirect them with the error_file and out_file fields, or turn them off with disable_logs: true. Empty them with pm2 flush and reopen them after rotation with pm2 reloadLogs.
Logs grow quietly, so plan for it. At a steady 1 MB of output per hour per process, the volume adds up fast, and a chatty app on a four-process cluster fills a small disk in weeks if nothing rotates it.
| Processes | Log volume per day | Log volume per week |
|---|---|---|
| 1 | 24 MB | 168 MB |
| 2 | 48 MB | 336 MB |
| 4 | 96 MB | 672 MB |
Rotate logs before they become a disk problem. Install the rotation module with pm2 install pm2-logrotate, or set up the system logrotate with sudo pm2 logrotate -u youruser, which writes a configuration that keeps 12 weekly rotations with compression. Either way, check the disk on a small VPS once a month: a log directory nobody watches is how a 20 GB disk fills up at 2 a.m.
How Do You Monitor a PM2 App?
Monitoring starts with two commands you already have. pm2 list is the quick status check: it shows every app, its id, its online or errored state, how many times it has restarted, and its CPU and memory use. pm2 monit opens a live terminal dashboard with per-process CPU, memory, and a scrolling log view, and since PM2 7.1.0 it preloads the last 200 log lines per process so you see the recent past, not just the live stream.
For one process in detail, pm2 describe 0 prints its script path, arguments, environment, uptime, restart count, and log paths. For automation, pm2 jlist prints the whole process list as raw JSON, which a monitoring script or an uptime checker can parse. PM2 also offers a hosted dashboard, PM2 Plus, which opens with pm2 plus and watches apps across several servers from a web page, and a newer terminal dashboard, pm2 dash, mentioned in the 7.1.0 release notes.
Watch the restart count first. An app with a restart count that climbs every hour is crashing in a loop, and PM2 will keep restarting it until it passes 16 consecutive unstable restarts, at which point the app is marked errored and left alone. A rising restart count is the signal to read the logs, not to restart the app again: the logs will usually name the error on the first line.
How Do You Update Node.js Without Breaking PM2?
Updating Node.js under a running PM2 is the one upgrade that quietly does nothing. The PM2 daemon holds the Node binary it started with, so after you install a new Node version, pm2 restart all does not pick up the new Node binary: it restarts your apps with the old one. This is sometimes called the PM2 state trap, and it bit people during the October 2026 Node.js security release, when high-severity fixes forced upgrades from Node 22.18.0 to 22.23.4.
The fix is pm2 update. Run it after the Node upgrade: it updates the in-memory PM2 daemon to the new Node binary and restores your processes.
The full order for a Node upgrade on a VPS is: install the new Node version, run pm2 update, confirm with pm2 list, and then regenerate the startup script with pm2 unstartup followed by pm2 startup, because the startup unit still points at the old Node path. Skip the last step and the next reboot brings back the old Node.
The same order applies to deploying new code. Pull the new code, then reload rather than restart: pm2 reload my-app hands traffic to the new process with zero downtime for a networked app.
If you deploy from a Git repository, our Git beginner guide covers the clone and pull commands you will run before the reload. And never mix a Node upgrade with a code deploy in one step: upgrade Node, verify the apps are healthy, then deploy the code, so you know which change broke things if something does.
PM2 vs systemd vs Docker: Which One Should You Use?
The pm2 vs systemd question comes up on every VPS, and the honest answer is that they solve different problems. systemd is the init system that starts services when the server boots, Docker is a container platform that packages an app with its environment, and PM2 is a process manager that keeps a Node.js app alive and balances it across CPUs. PM2 sits on top of systemd for boot survival, and it can run inside Docker, but it is not a replacement for either one.
| Question | PM2 | systemd | Docker |
|---|---|---|---|
| What is it? | Node.js process manager | Linux init and service manager | Container platform |
| Keeps a crashed app alive? | Yes, automatic restart built in | Yes, with a Restart policy | Yes, with a restart policy |
| Cluster mode and load balancing? | Yes, -i max | No | Yes, with replicas or Compose scale |
| Zero-downtime reload? | Yes, for networked apps | No, restart only | Rolling update, not built in |
| Best for | A Node.js app on a plain VPS | Any system service, any language | Isolated, repeatable deployments |
| Needs what to boot? | A systemd unit from pm2 startup | Nothing extra | The Docker daemon |
Choose PM2 when you run a Node.js app on a plain VPS and want the simplest path to auto-restart, cluster mode, and logs in one tool. Choose a plain systemd unit when the service is not Node.js, or when you want the fewest moving parts and one config file per service.
Choose Docker when you need the same app to run identically on your laptop and your server, or when you run several apps that must not share dependencies. Our Docker beginner guide covers that path if you go that way. And if you put the app behind a domain, our Nginx beginner guide covers the reverse proxy that should sit in front of PM2.
What Are the Common PM2 Mistakes?
Most PM2 problems are not bugs: they are one of a handful of setup mistakes, and each one has a one-line fix. The table below lists the mistakes beginners make most, what happens, and what to do instead.
| Mistake | What happens | Do this instead |
|---|---|---|
Running pm2 startup but forgetting pm2 save | The app does not come back after a reboot. | Run both commands, in that order. |
Using pm2 restart for every deploy | Every deploy drops live requests for a moment. | Use pm2 reload for networked apps. |
| Expecting cluster mode on a 1-vCPU VPS | -i max starts one process. | Size the VPS first, then cluster. |
Turning on --watch in production | Any file change restarts the live app. | Keep watch mode for development only. |
| Upgrading Node and only restarting apps | Apps keep running on the old Node binary. | Run pm2 update, then regenerate startup. |
| Never rotating logs | $HOME/.pm2/logs fills the disk. | Install pm2-logrotate on day one. |
Starting apps without --name | Two apps called app.js are hard to tell apart. | Name every app at start time. |
| Keeping sessions in process memory | Users lose their session when the next request hits another instance. | Store sessions in Redis or a database. |
One more thing to get right is security. PM2 7.0.0 fixed several vulnerabilities, including a regular-expression denial-of-service issue tracked as CVE-2025-5891 and command-injection fixes, so keep PM2 updated the same way you keep Node.js updated.
And do not confuse PM2 with an unrelated third-party panel: CVE-2026-72573, an operating-system command injection reported in August 2026, belongs to a separate project called pm2panel, not to PM2 itself. Finally, CodexFuse does not offer a PM2 tool: everything in this guide runs on your own server, with the commands above.
Sources
- PM2 official docs, "Quick Start" — install, start, restart, reload, stop, delete, list, monit, and update commands. https://pm2.keymetrics.io/docs/usage/quick-start/ — fetched 6 November 2026.
- PM2 official docs, "Startup" — pm2 startup, pm2 save, pm2 resurrect, pm2 unstartup, systemd units, and the Node-path note. https://pm2.keymetrics.io/docs/usage/startup/ — fetched 6 November 2026.
- PM2 official docs, "Cluster Mode" — -i max, one process per CPU, automatic load balancing, stateless apps, and graceful shutdown. https://pm2.keymetrics.io/docs/usage/cluster-mode/ — fetched 6 November 2026.
- PM2 official docs, "Application Declaration" — ecosystem file forms, the attributes table, --only, and --env. https://pm2.keymetrics.io/docs/usage/application-declaration/ — fetched 6 November 2026.
- PM2 official docs, "Log Management" — default log paths, pm2 logs flags, flush, reloadLogs, and log rotation. https://pm2.keymetrics.io/docs/usage/log-management/ — fetched 6 November 2026.
- PM2 GitHub repository — project definition, AGPL 3.0 license, Node 18 and newer requirement, more than 43,000 stars. https://github.com/Unitech/pm2 — fetched 6 November 2026.
- PM2 GitHub releases — 7.1.2 published 3 November 2026, 7.1.0 features, 7.0.0 Node 18 floor and security fixes. https://github.com/Unitech/pm2/releases — fetched 6 November 2026.
- npm registry — pm2 latest version 7.1.2, confirmed through the registry API. https://registry.npmjs.org/pm2/latest — fetched 6 November 2026.
- Node.js previous releases — current LTS lines Node 22 Jod, Node 24 Krypton, and Node 26 Lithium. https://nodejs.org/en/about/previous-releases — fetched 6 November 2026.
- Pickbox, "Deploy Node.js to DigitalOcean: Zero to Production Guide" — nvm, PM2 start, startup, save, Nginx, and HTTPS on a VPS. https://pickbox.dev/blog/deployment/deploying-nodejs-app-to-digitalocean-guide — fetched 6 November 2026.
- DigitalOcean, "How To Set Up a Node.js Application for Production on Ubuntu 22.04" — Node, PM2, Nginx, and Let's Encrypt on Ubuntu 22.04, published 26 April 2022. https://www.digitalocean.com/community/tutorials/how-to-set-up-a-node-js-application-for-production-on-ubuntu-22-04 — fetched 6 November 2026.
- Rafli, "Upgrading Node.js Under Fire: Fixing High-Severity CVEs and Dodging the PM2 State Trap" — the pm2 restart all trap and the October 2026 Node.js security release, published 22 October 2026. https://rafligoodid.medium.com/upgrading-node-js-under-fire-fixing-high-severity-cves-and-dodging-the-pm2-state-trap-724ef098a5ec — fetched 6 November 2026.
Frequently Asked Questions
Is PM2 free?
Yes. PM2 is free and open source under the GNU Affero General Public License 3.0, and you can install it with npm and use it on as many servers as you like. The separate hosted dashboard, PM2 Plus, is a Keymetrics service with its own plans; the process manager itself costs nothing.
Does PM2 restart my app when the server reboots?
Yes, but only if you complete both steps: run pm2 startup and then run the command it prints, and then run pm2 save to freeze the process list. Startup alone registers PM2 with the boot system but saves no apps, and save alone stores the list without registering PM2. Both are needed for the app to come back after a reboot.
How many PM2 instances should I run on a 1-vCPU VPS?
One. Cluster mode with -i max runs one process per CPU, so a 1-vCPU VPS gets exactly one instance.
Adding instances by hand on one CPU does not add speed: the processes would share the same core. Upgrade the VPS to more CPUs first, then let -i max scale with the hardware.
What is the difference between pm2 restart and pm2 reload?
pm2 restart kills the process and starts it again, so there is a brief downtime. pm2 reload starts a new process, moves traffic to it, and then stops the old one, which gives zero downtime for networked apps. For a worker or a script that does not serve requests, reload behaves like a restart, so use restart for those.
Can PM2 replace Nginx or systemd?
No. PM2 is a process manager, not a web server and not an init system.
It keeps your Node.js app alive and balances it across CPUs, but public traffic still needs a reverse proxy such as Nginx in front, and PM2 itself relies on a systemd unit created by pm2 startup to come back after a reboot. Use PM2 for the app, Nginx for the traffic, and systemd for the boot.


