PM2 is a production process manager for Node.js and Bun applications with a built-in load balancer. It keeps your applications alive forever, reloads them with zero downtime, manages their logs, and lets you control everything from a single command-line tool. PM2 can also run Python, Ruby, bash, and any binary in your $PATH.
Starting an application in production is as simple as:
pm2 start app.js
Your app is now daemonized, monitored, and kept alive across crashes and server reboots.
This guide covers everything you need to install PM2, run and manage your applications, scale them across CPU cores, stream their logs, survive server restarts, and troubleshoot the common issues. It is written against PM2 7.0.1 and is written from the source code.
Note
PM2 manages a background daemon (internally called "God") that owns your application processes. The
pm2command you type is a thin client that talks to this daemon. The daemon keeps running after your terminal closes — that is what keeps your apps alive.
1. Quick Start
Install PM2 globally, start an app, and inspect it — three commands:
npm install pm2 -g
pm2 start app.js
pm2 ls
You will see a process table like this:
┌────┬────────────────────┬──────────┬──────┬───────────┬──────────┬──────────┐
│ id │ name │ mode │ ↺ │ status │ cpu │ memory │
├────┼────────────────────┼──────────┼──────┼───────────┼──────────┼──────────┤
│ 0 │ app │ fork │ 0 │ online │ 0% │ 41.2mb │
└────┴────────────────────┴──────────┴──────┴───────────┴──────────┴──────────┘
From here, the everyday commands are:
pm2 logs # stream logs from all apps
pm2 restart app # restart the app named "app"
pm2 stop app # stop it (keeps it in the list)
pm2 delete app # stop and remove it from the list
pm2 monit # live dashboard of CPU/memory/logs
Tip
To make PM2 and your apps survive a server reboot, run
pm2 startuponce, thenpm2 saveafter your apps are running. See Section 8.
2. Installation
2.1 Requirements
| Requirement | Detail |
|---|---|
| Node.js | 18.0.0 or newer (declared in engines) |
| Bun | 1.1.25 or newer (PM2 refuses to start on older Bun — cluster support) |
| Operating systems | Linux, macOS, Windows |
| Privileges | A normal user account. Root/sudo is only needed to install a boot startup script |
2.2 Install with a package manager
npm
npm install pm2 -g
Bun
bun install pm2 -g
Yarn
yarn global add pm2
After installation, confirm the version:
pm2 --version
7.0.1
2.3 Bun-only systems
If you have Bun installed but no Node.js, PM2's #!/usr/bin/env node shebang has nothing to resolve. Symlink node to bun so the shebang lands on Bun's Node-compatibility runtime:
sudo ln -s $(which bun) /usr/local/bin/node
Warning
PM2 will throw
PM2 cannot run on Bun version < 1.1.25 (cluster support)and exit if your Bun is older than 1.1.25. Upgrade Bun before installing PM2.
2.4 Installing a development build
To run the bleeding-edge development branch instead of the npm release:
npm install git://github.com/Unitech/pm2.git#development -g
2.5 The four binaries
Installing PM2 puts four executables on your PATH. You will mostly use pm2, but the others matter for development and containers:
| Binary | Purpose |
|---|---|
pm2 |
The main process manager and CLI (everything in this guide) |
pm2-dev |
Development mode: starts one app, watches for file changes, streams logs in the foreground |
pm2-runtime |
A drop-in replacement for node in containers — runs in the foreground and exits with the app |
pm2-docker |
Alias of pm2-runtime, kept for backward compatibility |
Note
PM2 stores all of its state — logs, pids, the saved process list, and its control sockets — in a home directory at
~/.pm2. You can relocate it with thePM2_HOMEenvironment variable (see Section 13).
3. Starting Applications
The pm2 start command is the entry point for almost everything. It accepts a script, an application name, a process id, an ecosystem config file, or a list of these.
pm2 start <name|namespace|file|ecosystem|id...>
3.1 Start a script
pm2 start app.js
[PM2] Starting /home/user/app/app.js in fork_mode (1 instance)
[PM2] Done.
3.2 Give the app a name
By default PM2 names the process after the script file (without its extension), e.g. app.js becomes app. Override it with --name:
pm2 start app.js --name api
Tip
Always name your apps explicitly in production. You will be typing
pm2 restart apifar more often than you expect, and a clear name beats a numeric id.
3.3 Start non-Node programs
PM2 is not limited to Node.js. Use --interpreter to run any language, or --interpreter none to run a binary directly:
pm2 start app.py --interpreter python3
pm2 start worker.rb --interpreter ruby
pm2 start script.sh --interpreter bash
pm2 start ./my-binary --interpreter none
The -x / --execute-command flag is shorthand for running a program with the fork system.
3.4 Pass arguments to your app
Everything after a -- separator is passed through to your script, not to PM2:
pm2 start app.js --name api -- --port 8080 --verbose
Here --port 8080 --verbose are your application's arguments; --name api is PM2's.
3.5 Pass arguments to the interpreter (Node)
Use --node-args (alias --interpreter-args) for flags that go to Node itself:
pm2 start app.js --node-args="--max-old-space-size=2048 --trace-warnings"
3.6 Start from standard input
Pipe a JSON process definition straight into pm2 start -:
cat process.json | pm2 start -
3.7 Restart-on-start behavior
If you pm2 start a script that is already in the process list and currently stopped or errored, PM2 restarts it instead of creating a duplicate. To force a brand-new second instance of the same script, add -f / --force:
pm2 start app.js -f
[PM2][ERROR] Script already launched, add -f option to force re-execution
(That error is what you see without -f.)
4. Managing Running Applications
Once apps are running, you manage them by name, id, namespace, the keyword all, or a regex wrapped in slashes (e.g. /api-.*/).
4.1 The core lifecycle commands
| Command | What it does |
|---|---|
pm2 restart <app> |
Kill and start the process again (brief downtime) |
pm2 reload <app> |
Zero-downtime rolling restart (cluster mode only — see Section 5) |
pm2 stop <app> |
Stop the process but keep it in the list (status becomes stopped) |
pm2 delete <app> (alias pm2 del) |
Stop and remove the process from the list entirely |
pm2 start <app> |
Start a stopped process again |
Examples:
pm2 restart api
pm2 stop 0
pm2 restart all
pm2 delete all
pm2 restart /worker-.*/ # regex: every app whose name matches
Important
stopkeeps the application's configuration in PM2's list so you can start it again later.deleteforgets it entirely — you would need the originalpm2 startcommand (or your ecosystem file) to bring it back.
4.2 Restart, reload, and the environment
By default, restart and reload do not refresh environment variables — your app keeps the environment it was first started with. This is intentional, so an accidental restart never silently changes config. To pull in the current environment, add -a / --update-env:
pm2 restart api --update-env
Use --update-env to update environment variables
(That message is PM2 reminding you that the environment was left unchanged.)
4.3 Scaling instances in cluster mode
pm2 scale api 4 # run exactly 4 instances of "api"
pm2 scale api +2 # add 2 more instances
4.4 Restart counters and limits
PM2 tracks how many times each app has restarted. Reset the counters with:
pm2 reset api
pm2 reset all
Control automatic-restart behavior at start time:
| Flag | Effect |
|---|---|
--max-restarts [count] |
Give up after restarting this many times |
--restart-delay <ms> |
Wait this long between restarts |
--exp-backoff-restart-delay <ms> |
Use exponential backoff between restarts (delay grows after each failure) |
--no-autorestart |
Never restart the app automatically |
--stop-exit-codes <codes...> |
Treat these exit codes as clean — do not autorestart |
--max-memory-restart <mem> |
Restart if memory exceeds this (e.g. 300M, 1G) |
pm2 start worker.js --max-memory-restart 300M --exp-backoff-restart-delay 100
Tip
--exp-backoff-restart-delayis the best choice for a flapping app. Instead of hammering restarts in a tight loop, PM2 progressively widens the delay, giving a failing dependency (a database, an upstream API) time to recover. The backoff resets after the app stays up for 30 seconds.
4.5 Sending signals
pm2 sendSignal SIGUSR2 api # by name
pm2 sendSignal SIGUSR2 0 # by id
4.6 Sending data to a process
pm2 send 0 "hello from pm2" # write a line to process 0's stdin
pm2 attach 0 # attach your terminal to process 0's stdin/stdout
Note
pm2 sendandpm2 attachonly work for apps running in fork mode. Cluster-mode processes do not have a shared stdin to write to.
5. Cluster Mode: Load Balancing & Zero-Downtime
Cluster mode runs multiple identical copies of your Node.js app and load-balances incoming HTTP/TCP/UDP connections across them using Node's built-in cluster module. This increases throughput on multi-core machines and lets you reload with zero downtime.
5.1 Start in cluster mode
The -i / --instances flag turns on cluster mode:
pm2 start api.js -i 4 # 4 instances
pm2 start api.js -i max # one instance per CPU core
pm2 start api.js -i -1 # all cores except one
pm2 start api.js -i 0 # same as 'max'
[PM2] Starting /home/user/api.js in cluster_mode (4 instances)
[PM2] Done.
Important
Cluster mode requires that your application can run as multiple independent processes — typically a stateless HTTP/TCP server. Each instance receives a different value in the
NODE_APP_INSTANCEenvironment variable (configurable viainstance_var) so you can tell them apart.
5.2 Zero-downtime reload
pm2 reload api
pm2 reload all
reload restarts instances one at a time: it spawns a new worker, waits for it to start listening, then shuts down the old one. At least one instance is always serving traffic, so users never see a dropped connection.
By default PM2 waits for the new worker's listening event. If your app needs to do setup work before it is truly ready, opt into the explicit ready signal:
pm2 start api.js -i 4 --wait-ready --listen-timeout 10000
Then, inside your application, tell PM2 when you are ready:
// inside api.js, after your server is fully initialized
process.send('ready');
Warning
pm2 reloadonly performs a true zero-downtime rolling restart for apps in cluster mode. For a fork-mode app,reloadfalls back to a plainrestart(with downtime). If zero-downtime matters, start with-i.
5.3 Graceful shutdown
When PM2 stops a process it sends SIGINT first, waits up to kill_timeout milliseconds (default 1600ms), then sends SIGKILL. Handle SIGINT to close connections cleanly:
process.on('SIGINT', () => {
server.close(() => process.exit(0));
});
Give slow-draining apps more time with --kill-timeout:
pm2 start api.js --kill-timeout 5000
You can also ask PM2 to shut the app down via an IPC message instead of a signal — useful on Windows where signals are unreliable:
pm2 start api.js --shutdown-with-message
Your app then listens for the shutdown message:
process.on('message', (msg) => {
if (msg === 'shutdown') { /* clean up */ process.exit(0); }
});
6. Inspecting Processes
6.1 Listing
| Command | Output |
|---|---|
pm2 list (aliases ls, l, ps, status) |
The standard process table |
pm2 jlist |
The full process list as raw JSON (for scripts) |
pm2 prettylist |
The process list as prettified JSON |
pm2 list --mini-list (-m) |
A compact, unformatted list |
pm2 ls
pm2 jlist | jq '.[].name'
Sort the list with --sort:
pm2 ls --sort name:asc
pm2 ls --sort memory:desc
6.2 Describing one process
pm2 describe (aliases desc, info, show) prints every detail of a process — its script path, log files, environment, restart count, uptime, exec mode, and more:
pm2 describe api
pm2 show 0
Get just the pid, or the environment, of a process:
pm2 pid api # print the OS pid(s)
pm2 id api # print PM2's internal id by name
pm2 env 0 # print the environment variables of process 0
6.3 Live monitoring dashboards
pm2 monit # terminal dashboard: CPU, memory, and logs per app
pm2 dashboard # (alias 'dash') richer dashboard with monitoring + logs
pm2 imonit # legacy termcaps monitor
6.4 Host vitals (system monitoring)
Enable a monitoring "speedbar" for the host machine's CPU, memory, and disk:
pm2 set pm2:sysmonit true
pm2 update
Then query system information directly:
pm2 slist # system info as JSON
pm2 slist --tree # as a readable tree
pm2 sysmonit # start the system-monitoring daemon
7. Log Management
PM2 captures the stdout and stderr of every app and writes them to files in ~/.pm2/logs/, while also letting you stream them live.
7.1 Streaming logs
pm2 logs # stream logs from all apps
pm2 logs api # only the "api" app
pm2 logs --lines 200 # show the last 200 lines first (default 15)
pm2 logs --err # only stderr
pm2 logs --out # only stdout
pm2 logs --timestamp # prefix each line with a timestamp
pm2 logs --json # structured JSON log lines
pm2 logs --format # key=value formatted output
pm2 logs --raw # raw output, no PM2 framing
pm2 logs --nostream # print recent logs and exit (don't follow)
pm2 logs api --highlight ERROR # highlight matching text
7.2 Managing log files
pm2 flush # empty all log files
pm2 flush api # empty only this app's logs
pm2 reloadLogs # reopen log files (use after external log rotation)
7.3 Log formatting at start time
| Flag | Effect |
|---|---|
--log <path> (-l) |
Combine stdout and stderr into one file |
--output <path> (-o) |
stdout log file |
--error <path> (-e) |
stderr log file |
--log-type json |
Write logs as JSON objects |
--log-date-format <fmt> |
Prefix each line with a timestamp in this format |
--time |
Enable timestamped logging |
--merge-logs |
In cluster mode, merge all instances into one pair of log files |
--disable-logs |
Do not store logs at all |
pm2 start api.js -i 4 --merge-logs --log-date-format "YYYY-MM-DD HH:mm:ss"
Tip
Without
--merge-logs, a 4-instance cluster app produces 8 log files (4 out + 4 err). With it, you get just one stdout and one stderr file — much easier totail.
7.4 Automatic log rotation
PM2's built-in log files grow forever. Install the pm2-logrotate module to cap and rotate them:
pm2 install pm2-logrotate
You can also drop a system logrotate configuration:
pm2 logrotate
8. Keeping Apps Alive Across Reboots (Startup & Save)
PM2 can generate an init-system service so that the daemon and your saved apps come back automatically after a reboot.
8.1 Generate and enable the startup script
pm2 startup
PM2 detects your init system and prints a command to run (usually with sudo). Run that command, and PM2 registers itself to boot.
Init systems supported: systemd, upstart, systemv, openrc, launchd, rcd, rcd-openbsd, smf. You can also name one explicitly:
pm2 startup systemd
pm2 startup launchd
Useful startup options:
| Flag | Purpose |
|---|---|
-u <username> |
Run the service as this user |
--hp <home path> |
Set the home path used in the generated script |
--service-name <name> |
Name the generated service |
--wait-ip |
Wait for full network connectivity before launching PM2 (systemd) |
8.2 Freeze the current process list
After your apps are running the way you want, save them so they are restored on boot:
pm2 save
[PM2] Saving current process list...
[PM2] Successfully saved in /home/user/.pm2/dump.pm2
Important
pm2 startuponly registers the boot hook. It does not record which apps to start —pm2 savedoes that. Always runpm2 saveafter starting or changing your apps, or they will not come back after a reboot.
8.3 Restore and remove
pm2 resurrect # manually restore the saved process list
pm2 unstartup # remove the boot hook
pm2 cleardump # empty the saved process list
pm2 save (which is an alias of pm2 dump) writes to ~/.pm2/dump.pm2 and keeps a .bak copy. If the live list is empty, PM2 refuses to overwrite a non-empty dump unless you force it:
pm2 dump --force
9. Ecosystem Configuration Files
Typing long pm2 start commands does not scale. An ecosystem file declares all your apps and their settings in one place. Generate a template with:
pm2 ecosystem # creates ecosystem.config.js
pm2 init # (alias)
pm2 ecosystem simple # a minimal template
A typical ecosystem.config.js:
module.exports = {
apps: [
{
name: 'api',
script: './api.js',
instances: 'max',
exec_mode: 'cluster',
watch: false,
max_memory_restart: '300M',
env: {
NODE_ENV: 'development',
PORT: 3000
},
env_production: {
NODE_ENV: 'production',
PORT: 8080
}
},
{
name: 'worker',
script: './worker.js',
instances: 1,
cron_restart: '0 3 * * *',
autorestart: true
}
]
};
Start, restart, or reload everything in the file:
pm2 start ecosystem.config.js
pm2 start ecosystem.config.js --only api # just one app
pm2 startOrRestart ecosystem.config.js
pm2 startOrReload ecosystem.config.js # zero-downtime
pm2 startOrGracefulReload ecosystem.config.js
9.1 Selecting an environment
The --env flag injects a named environment block (env_<name>) from the file:
pm2 start ecosystem.config.js --env production
pm2 restart api --env production --update-env
In the example above, --env production merges the env_production block over the base env.
Note
--envonly works when starting from an ecosystem file.pm2 reload api --env productionwithout the file will fail with "Using --env [env] without passing the ecosystem.config.js does not work".
9.2 Full field reference
These are the fields you can set per app. They mirror the options available on the pm2 start command line (in snake_case).
| Field | Default | Description |
|---|---|---|
name |
script name | Name used to reference the app |
script |
— | Path to the script or binary to run |
args |
— | Arguments passed to the script |
interpreter |
node |
Interpreter (python, ruby, bash, or none for a binary) |
interpreter_args / node_args |
— | Arguments passed to the interpreter |
cwd |
start dir | Working directory for the process |
instances |
1 |
Number of instances (use max or 0 for all cores) |
exec_mode |
fork |
fork or cluster |
watch |
false |
Restart on file change; can be an array of paths |
ignore_watch |
— | Paths/regexes to ignore while watching |
max_memory_restart |
— | Restart when memory exceeds this (e.g. 300M) |
max_restarts |
— | Max restarts before giving up |
min_uptime |
— | Minimum uptime before a start is considered successful |
restart_delay |
0 |
Delay (ms) before restarting after exit |
exp_backoff_restart_delay |
— | Exponential backoff base delay (ms) |
autorestart |
true |
Restart automatically on crash |
autostart |
true |
Start automatically when added |
stop_exit_codes |
— | Exit codes that skip autorestart |
cron_restart |
— | Cron pattern for scheduled restarts |
kill_timeout |
1600 |
ms to wait after SIGINT before SIGKILL |
listen_timeout |
— | ms to wait for the app to be ready after reload |
wait_ready |
false |
Wait for process.send('ready') instead of the listening event |
shutdown_with_message |
false |
Send a shutdown IPC message instead of a signal |
merge_logs |
false |
Merge cluster instance logs into one file |
log_date_format |
— | Timestamp format for log lines |
output / error |
~/.pm2/logs/... |
stdout / stderr log file paths |
disable_logs |
false |
Disable log storage |
log_type |
— | json for structured logs |
namespace |
default |
Group apps under a namespace |
env |
— | Environment variables to inject |
port |
— | Shortcut to inject a PORT env variable |
instance_var |
NODE_APP_INSTANCE |
Env var holding each instance's id |
increment_var |
— | Env var incremented per cluster instance |
filter_env |
— | Strip matching env vars from the process |
uid / gid |
— | Run the process as this user / group |
source_map_support |
— | Force source-map support on or off |
9.3 Writing a config from the command line
The -w / --write flag dumps the resolved configuration of a pm2 start to a <name>-pm2.json file in the current directory, which you can keep as a starting point:
pm2 start api.js --name api -i 4 --write
10. Namespaces
Namespaces group related apps so you can act on them together. Assign one at start time:
pm2 start api.js --namespace backend
pm2 start worker.js --namespace backend
Then operate on the whole group by name:
pm2 restart backend
pm2 stop backend
pm2 delete backend
11. Development Mode (pm2-dev)
For local development, pm2-dev runs a single app in the foreground, watches for file changes, restarts automatically, and streams logs to your terminal. Press Ctrl-C to stop everything.
pm2-dev app.js
pm2-dev start app.js
Options:
| Flag | Effect |
|---|---|
--raw |
Raw log output |
--timestamp |
Prefix logs with a timestamp |
--node-args <args> |
Pass arguments to Node |
--ignore [files] |
Comma-separated files to ignore while watching (node_modules is always ignored) |
--post-exec [cmd] |
Run a command after each restart |
--silent-exec |
Suppress the post-exec command's output |
--interpreter <name> |
Use a non-Node interpreter |
--env [name] |
Select an env_<name> block |
--auto-exit |
Exit when all processes are stopped/errored or none launched |
Note
pm2-devuses a separate home directory (~/.pm2-dev) so it never interferes with your productionpm2process list.
11.1 Watch mode in the main CLI
You can also enable watching with the regular pm2 command:
pm2 start api.js --watch
pm2 start api.js --watch --ignore-watch="node_modules logs" --watch-delay 1000
Toggle watching on an already-running app:
pm2 restart api --watch # turn watching on/off
pm2 stop api --watch # stop watching
12. Running in Containers (pm2-runtime)
In a Docker container you usually want a foreground process that exits when your app dies, so the container's lifecycle matches the app's. That is exactly what pm2-runtime provides — a drop-in replacement for node.
FROM node:20
WORKDIR /app
COPY . .
RUN npm install pm2 -g && npm install
CMD ["pm2-runtime", "npm", "--", "start"]
Or point it at your app or ecosystem file directly:
CMD ["pm2-runtime", "app.js", "-i", "max"]
CMD ["pm2-runtime", "ecosystem.config.js"]
Key pm2-runtime options:
| Flag | Effect |
|---|---|
-i --instances <n> |
Run N load-balanced instances |
--watch |
Watch and restart on file change |
--no-autorestart |
Disable automatic restart |
--max-memory-restart <mem> |
Restart above a memory threshold |
--web [port] |
Expose a process web API (default port 9615) |
--delay <seconds> |
Delay startup of the config by N seconds |
--no-auto-exit |
Do not exit when all apps are stopped |
--json / --format / --raw |
Log output format |
--only <app> |
Only run one app from the configuration |
Tip
pm2-runtimestreams your app's logs to the container's stdout/stderr, which is what Docker, Kubernetes, and your log aggregator expect. There is no separatepm2 logsstep inside the container.
13. Environment Variable Reference
These environment variables change PM2's behavior. The most useful ones for everyday users:
| Variable | Default | Purpose |
|---|---|---|
PM2_HOME |
~/.pm2 |
Directory for PM2's logs, pids, sockets, and saved list |
PM2_KILL_TIMEOUT |
1600 |
ms between SIGINT and SIGKILL when stopping a process |
PM2_KILL_SIGNAL |
SIGINT |
Signal sent first when stopping a process |
PM2_GRACEFUL_TIMEOUT |
8000 |
ms to wait for graceful shutdown during reload |
PM2_GRACEFUL_LISTEN_TIMEOUT |
3000 |
ms to wait for the listening/ready event on reload |
PM2_CONCURRENT_ACTIONS |
2 |
How many apps PM2 starts/restarts/reloads in parallel |
PM2_LOG_DATE_FORMAT |
YYYY-MM-DDTHH:mm:ss |
Default timestamp format for logs |
PM2_DISCRETE_MODE |
— | Suppress the PM2 startup banner |
PM2_SILENT |
— | Hide all PM2 messages (same as --silent) |
PM2_HOME overrides |
— | Individual paths can be overridden with PM2_* variants |
Note
Setting
PM2_HOMElets you run multiple independent PM2 instances on one machine (for example, one per deployed service or per user). Each instance has its own daemon, sockets, and process list.
14. Updating PM2
When you upgrade the PM2 package, the in-memory daemon is still running the old version. Refresh it without losing your apps:
npm install pm2@latest -g
pm2 update
>>>>>>>>>> PM2 updated
pm2 update saves your process list, restarts the daemon with the new code, and restores all your apps. PM2 also warns you automatically when the running daemon is out of date:
>>>> In-memory PM2 is out-of-date, do:
>>>> $ pm2 update
pm2 deepUpdate performs a deeper update of PM2 itself when a plain update is not enough.
15. Application Versioning & Deployment
If your app is a git checkout, PM2 can move it along its history and restart it:
pm2 pull api # pull the latest commit and restart
pm2 pull api <commit_id> # check out a specific commit and restart
pm2 forward api # move to the next commit
pm2 backward api # roll back to the previous commit
PM2 also ships a declarative deploy system driven by the deploy block of an ecosystem file:
pm2 deploy ecosystem.config.js production setup
pm2 deploy ecosystem.config.js production
16. Modules
PM2 modules are background apps managed by PM2 itself (log rotation, monitoring probes, and more).
pm2 install pm2-logrotate # install and run a module
pm2 install <git:// url> # install from a git URL
pm2 module:update pm2-logrotate # update a module
pm2 uninstall pm2-logrotate # stop and remove a module
pm2 module:generate my-module # scaffold a new module in the current folder
Module configuration uses the set / get commands with a module:key syntax:
pm2 set pm2-logrotate:max_size 10M
pm2 set pm2-logrotate:retain 30
pm2 get pm2-logrotate:max_size
pm2 conf pm2-logrotate # view/edit module config
pm2 unset pm2-logrotate:max_size
17. Serving Static Files
PM2 can run a static HTTP file server as a managed process:
pm2 serve ./public 8080
pm2 serve ./dist 8080 --spa # always serve index.html
pm2 serve ./public 8080 --basic-auth-username admin --basic-auth-password secret
pm2 serve ./files 8080 --ftp # directory listing
pm2 serve (alias pm2 expose) keeps the server alive like any other app and shows up in pm2 ls.
18. PM2+ Monitoring (Optional)
PM2+ is a hosted dashboard (at app.pm2.io) for monitoring apps across servers. It is entirely optional.
pm2 link <secret> <public> # connect this machine to a PM2+ bucket
pm2 monitor # start monitoring (opens the dashboard flow)
pm2 unmonitor # stop monitoring
pm2 open # open the dashboard in a browser
pm2 unlink # disconnect from PM2+
pm2 plus # enable PM2+ (alias 'register')
pm2 login # log in to PM2+
pm2 logout # log out
Note
You never need PM2+ to use PM2. All process management, clustering, logging, and startup features described in this guide work completely offline.
19. CPU & Memory Profiling
pm2 profile:cpu 10 # profile CPU for 10 seconds
pm2 profile:mem 10 # sample heap memory for 10 seconds
Deep monitoring (V8 metrics, event-loop inspector, transaction tracing) can be enabled at start:
pm2 start api.js --deep-monitoring
This is equivalent to --v8 --event-loop-inspector --trace. Tracing requires OpenTelemetry packages:
pm2 install-otel # install OpenTelemetry tracing packages
pm2 uninstall-otel # remove them
20. Shell Completion
Install tab-completion for the pm2 command:
pm2 completion install
After reloading your shell, pressing Tab completes commands and your app names.
21. Generating a Diagnostic Report
When asking for help, attach a full diagnostic report:
pm2 report
This prints PM2's version, your Node version, environment, managed-app count, and daemon uptime — exactly what the maintainers ask for on the issue tracker.
22. Stopping the Daemon
To stop the PM2 daemon entirely — killing all managed apps with it:
pm2 kill
[PM2] [v] All Applications Stopped
[PM2] [v] PM2 Daemon Stopped
The daemon starts again automatically the next time you run any pm2 command. To check whether it is alive without starting apps:
pm2 ping
[PM2] pong
23. Command Quick Reference
Lifecycle
| Command | Description |
|---|---|
pm2 start <app> |
Start and daemonize an app |
pm2 restart <app> |
Restart an app |
pm2 reload <app> |
Zero-downtime reload (cluster mode) |
pm2 stop <app> |
Stop an app (keep in list) |
pm2 delete <app> |
Stop and remove an app |
pm2 scale <app> <n> |
Scale a cluster app to N instances |
pm2 reset <app> |
Reset restart counters |
Inspection
| Command | Description |
|---|---|
pm2 list / ls / ps / status |
List processes |
pm2 jlist / prettylist |
List as JSON |
pm2 describe <app> / show / info |
Detail one process |
pm2 monit / dashboard |
Live dashboard |
pm2 logs |
Stream logs |
pm2 pid <app> / id <app> / env <id> |
Pid / id / environment |
pm2 slist |
System info as JSON |
Persistence & system
| Command | Description |
|---|---|
pm2 startup / unstartup |
Manage the boot hook |
pm2 save / dump |
Freeze the process list |
pm2 resurrect |
Restore the saved list |
pm2 update / deepUpdate |
Refresh the in-memory daemon |
pm2 kill / ping |
Stop / probe the daemon |
Config, logs & modules
| Command | Description |
|---|---|
pm2 ecosystem / init |
Generate a config file |
pm2 set / get / unset / conf |
Manage configuration |
pm2 flush / reloadLogs |
Manage log files |
pm2 install / uninstall / module:update |
Manage modules |
pm2 serve <path> <port> |
Serve static files |
Getting help
pm2 --help # all commands and options
pm2 <command> --help # help for one command
pm2 examples # printed usage examples
24. Troubleshooting
Error: Script already launched, add -f option to force re-execution
Cause — You tried to pm2 start a script that is already in the process list and online. PM2 prevents accidental duplicates.
Solution — To restart it, use pm2 restart <name>. To deliberately run a second copy, add -f:
pm2 start app.js -f
Prevention — Reference apps by name (pm2 restart api) instead of re-running pm2 start with the script path.
Error: Interpreter <name> is NOT AVAILABLE in PATH
Cause — PM2 cannot find the interpreter you specified (or the one inferred from the file extension). This is the most common cause behind issues like a Python or Ruby script that "stopped working" after a PM2 upgrade.
Solution — Make sure the interpreter is installed and on the PATH of the user running PM2. Specify it explicitly:
pm2 start app.py --interpreter python3
which python3 # confirm it resolves
Prevention — Pin the interpreter in your ecosystem file's interpreter field so it does not rely on extension inference.
Problem: pm2 start npm -- start does not behave as expected
Cause — Starting an npm script through PM2 requires passing the npm sub-command after the -- separator so it reaches npm rather than PM2.
Solution — Use the explicit form, and name it:
pm2 start npm --name myapp -- start
In containers, the same pattern works with pm2-runtime:
pm2-runtime npm -- start
Prevention — Always name the process and keep the -- separator before the npm sub-command.
Problem: My app restarts in an endless loop
Cause — The app crashes immediately on start (a syntax error, a missing env var, a port already in use), so PM2 keeps restarting it. The restart count (↺ column) climbs rapidly.
Solution — Read the logs to find the real error, then cap the restarts and add a backoff while you fix it:
pm2 logs api --lines 100
pm2 start api.js --max-restarts 5 --exp-backoff-restart-delay 200
Prevention — Set min_uptime and max_restarts in your ecosystem file so a broken deploy fails loudly instead of looping forever.
Problem: pm2 reload causes downtime
Cause — reload only does a true zero-downtime rolling restart in cluster mode. A fork-mode app falls back to a plain restart.
Solution — Start the app in cluster mode so reload becomes rolling:
pm2 start api.js -i max
pm2 reload api
Prevention — Use exec_mode: 'cluster' and instances in your ecosystem file for any HTTP service that needs zero-downtime deploys.
Problem: Reloaded app drops connections even in cluster mode
Cause — PM2 considers a new worker ready on its listening event, but your app needs more setup time before it can serve traffic, so the old worker is killed too early.
Solution — Opt into the explicit ready signal and raise the timeout:
pm2 start api.js -i 4 --wait-ready --listen-timeout 10000
// signal readiness only when truly ready
process.send('ready');
Prevention — Set wait_ready: true and a generous listen_timeout in the ecosystem file.
Problem: My apps did not come back after a server reboot
Cause — Either the startup hook was never installed, or the process list was never saved.
Solution — Do both, in order:
pm2 startup # then run the sudo command it prints
pm2 start ... # start your apps
pm2 save # freeze the list
Prevention — Re-run pm2 save every time you change which apps are running.
Problem: PM2 commands behave strangely after an upgrade
Cause — The npm package was upgraded but the in-memory daemon is still the old version. PM2 prints a >>>> In-memory PM2 is out-of-date warning when this happens.
Solution —
pm2 update
Prevention — Run pm2 update immediately after every npm install pm2@latest -g.
Problem: Logs have filled the disk
Cause — PM2's built-in log files grow without bound.
Solution — Flush now and install rotation:
pm2 flush
pm2 install pm2-logrotate
pm2 set pm2-logrotate:max_size 10M
Prevention — Install pm2-logrotate as part of every server's standard setup.
Problem: Cannot send a line to a process / pm2 send does nothing
Cause — pm2 send and pm2 attach only work for fork-mode processes. Cluster-mode processes have no single shared stdin.
Solution — Start the target app in fork mode (omit -i) if you need to interact with its stdin.
Prevention — Reserve stdin-driven interaction for fork-mode apps.
Problem: PM2 will not start on a Bun-only system
Cause — Either Bun is older than 1.1.25, or there is no node binary for PM2's shebang to resolve.
Solution — Upgrade Bun, and symlink node to bun:
sudo ln -s $(which bun) /usr/local/bin/node
Prevention — Keep Bun at 1.1.25+ on production hosts.
25. Frequently Asked Questions
Q: Does PM2 only work with Node.js?
No. PM2 manages Python, Ruby, bash, and any binary via --interpreter (or --interpreter none for a raw executable). Node.js just gets extra features like cluster mode.
Q: What is the difference between restart and reload?
restart kills and re-spawns the process, causing brief downtime. reload performs a one-at-a-time rolling restart with zero downtime, but only in cluster mode.
Q: What is the difference between stop and delete?
stop halts the process but keeps it in PM2's list so you can start it again. delete removes it from the list entirely.
Q: Does the daemon keep running after I close my terminal? Yes. That is the whole point — the PM2 daemon is detached and keeps your apps running independently of any shell.
Q: How do I make my apps survive a reboot?
Run pm2 startup once (and the sudo command it prints), then pm2 save after your apps are running.
Q: How many instances should I run in cluster mode?
pm2 start app.js -i max uses one instance per CPU core, which is a good default for CPU-bound HTTP servers. Use -i -1 to leave one core for the OS.
Q: Where are my logs?
In ~/.pm2/logs/ by default, one -out.log and one -error.log per app (or per instance unless you use --merge-logs). Stream them with pm2 logs.
Q: How do I pass environment variables?
Put them in the env block of an ecosystem file, and use named blocks (env_production) selected with --env production. Remember to add --update-env on restart to apply changes.
Q: Why didn't my environment variable change take effect on restart?
By design, restart/reload keep the original environment. Add -a / --update-env to refresh it.
Q: Can I run multiple independent PM2 instances on one machine?
Yes. Set a different PM2_HOME for each — they get separate daemons, sockets, and process lists.
Q: How do I update PM2 without losing my running apps?
npm install pm2@latest -g then pm2 update. PM2 saves, restarts the daemon, and restores your apps.
Q: How do I report a bug?
Run pm2 report and attach its output to an issue at the PM2 GitHub repository.
Q: Do I need a PM2+ account? No. PM2+ is an optional hosted dashboard. Every feature in this guide works without it.