# PM2 User Guide

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:

```bash
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 `pm2` command 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:

```bash
npm install pm2 -g
pm2 start app.js
pm2 ls
```

You will see a process table like this:

```text
┌────┬────────────────────┬──────────┬──────┬───────────┬──────────┬──────────┐
│ id │ name               │ mode     │ ↺    │ status    │ cpu      │ memory   │
├────┼────────────────────┼──────────┼──────┼───────────┼──────────┼──────────┤
│ 0  │ app                │ fork     │ 0    │ online    │ 0%       │ 41.2mb   │
└────┴────────────────────┴──────────┴──────┴───────────┴──────────┴──────────┘
```

From here, the everyday commands are:

```bash
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 startup` once, then `pm2 save` after your apps are running. See [Section 8](#8-keeping-apps-alive-across-reboots-startup--save).

---

## 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"

    ```bash
    npm install pm2 -g
    ```

=== "Bun"

    ```bash
    bun install pm2 -g
    ```

=== "Yarn"

    ```bash
    yarn global add pm2
    ```

After installation, confirm the version:

```bash
pm2 --version
```

```text
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:

```bash
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:

```bash
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 the `PM2_HOME` environment variable (see [Section 13](#13-environment-variable-reference)).

---

## 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.

```bash
pm2 start <name|namespace|file|ecosystem|id...>
```

### 3.1 Start a script

```bash
pm2 start app.js
```

```text
[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`:

```bash
pm2 start app.js --name api
```

> [!TIP]
> Always name your apps explicitly in production. You will be typing `pm2 restart api` far 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:

```bash
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:

```bash
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:

```bash
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 -`:

```bash
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`:

```bash
pm2 start app.js -f
```

```text
[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](#5-cluster-mode-load-balancing--zero-downtime)) |
| `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:

```bash
pm2 restart api
pm2 stop 0
pm2 restart all
pm2 delete all
pm2 restart /worker-.*/      # regex: every app whose name matches
```

> [!IMPORTANT]
> `stop` keeps the application's configuration in PM2's list so you can start it again later. `delete` forgets it entirely — you would need the original `pm2 start` command (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`:

```bash
pm2 restart api --update-env
```

```text
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

```bash
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:

```bash
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`) |

```bash
pm2 start worker.js --max-memory-restart 300M --exp-backoff-restart-delay 100
```

> [!TIP]
> `--exp-backoff-restart-delay` is 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

```bash
pm2 sendSignal SIGUSR2 api      # by name
pm2 sendSignal SIGUSR2 0        # by id
```

### 4.6 Sending data to a process

```bash
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 send` and `pm2 attach` only 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:

```bash
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'
```

```text
[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_INSTANCE` environment variable (configurable via `instance_var`) so you can tell them apart.

### 5.2 Zero-downtime reload

```bash
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:

```bash
pm2 start api.js -i 4 --wait-ready --listen-timeout 10000
```

Then, inside your application, tell PM2 when you are ready:

```javascript
// inside api.js, after your server is fully initialized
process.send('ready');
```

> [!WARNING]
> `pm2 reload` only performs a true zero-downtime rolling restart for apps in **cluster mode**. For a fork-mode app, `reload` falls back to a plain `restart` (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:

```javascript
process.on('SIGINT', () => {
  server.close(() => process.exit(0));
});
```

Give slow-draining apps more time with `--kill-timeout`:

```bash
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:

```bash
pm2 start api.js --shutdown-with-message
```

Your app then listens for the `shutdown` message:

```javascript
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 |

```bash
pm2 ls
pm2 jlist | jq '.[].name'
```

Sort the list with `--sort`:

```bash
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:

```bash
pm2 describe api
pm2 show 0
```

Get just the pid, or the environment, of a process:

```bash
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

```bash
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:

```bash
pm2 set pm2:sysmonit true
pm2 update
```

Then query system information directly:

```bash
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

```bash
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

```bash
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 |

```bash
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 to `tail`.

### 7.4 Automatic log rotation

PM2's built-in log files grow forever. Install the `pm2-logrotate` module to cap and rotate them:

```bash
pm2 install pm2-logrotate
```

You can also drop a system `logrotate` configuration:

```bash
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

```bash
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:

```bash
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:

```bash
pm2 save
```

```text
[PM2] Saving current process list...
[PM2] Successfully saved in /home/user/.pm2/dump.pm2
```

> [!IMPORTANT]
> `pm2 startup` only registers the boot hook. It does **not** record which apps to start — `pm2 save` does that. Always run `pm2 save` after starting or changing your apps, or they will not come back after a reboot.

### 8.3 Restore and remove

```bash
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:

```bash
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:

```bash
pm2 ecosystem            # creates ecosystem.config.js
pm2 init                 # (alias)
pm2 ecosystem simple     # a minimal template
```

A typical `ecosystem.config.js`:

```javascript
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:

```bash
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:

```bash
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]
> `--env` only works when starting from an ecosystem file. `pm2 reload api --env production` without 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:

```bash
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:

```bash
pm2 start api.js --namespace backend
pm2 start worker.js --namespace backend
```

Then operate on the whole group by name:

```bash
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.

```bash
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-dev` uses a separate home directory (`~/.pm2-dev`) so it never interferes with your production `pm2` process list.

### 11.1 Watch mode in the main CLI

You can also enable watching with the regular `pm2` command:

```bash
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:

```bash
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`.

```dockerfile
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:

```dockerfile
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-runtime` streams your app's logs to the container's stdout/stderr, which is what Docker, Kubernetes, and your log aggregator expect. There is no separate `pm2 logs` step 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_HOME` lets 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:

```bash
npm install pm2@latest -g
pm2 update
```

```text
>>>>>>>>>> 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:

```text
>>>> 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:

```bash
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:

```bash
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).

```bash
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:

```bash
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:

```bash
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](https://app.pm2.io)) for monitoring apps across servers. It is entirely optional.

```bash
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

```bash
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:

```bash
pm2 start api.js --deep-monitoring
```

This is equivalent to `--v8 --event-loop-inspector --trace`. Tracing requires OpenTelemetry packages:

```bash
pm2 install-otel                 # install OpenTelemetry tracing packages
pm2 uninstall-otel               # remove them
```

---

## 20. Shell Completion

Install tab-completion for the `pm2` command:

```bash
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:

```bash
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:

```bash
pm2 kill
```

```text
[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:

```bash
pm2 ping
```

```text
[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

```bash
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`:

```bash
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:

```bash
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:

```bash
pm2 start npm --name myapp -- start
```

In containers, the same pattern works with `pm2-runtime`:

```bash
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:

```bash
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:

```bash
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:

```bash
pm2 start api.js -i 4 --wait-ready --listen-timeout 10000
```

```javascript
// 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:

```bash
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** —

```bash
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:

```bash
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`:

```bash
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.

---

_Generated June 2026 from Unitech/pm2 at ba62cae. AI-generated documentation. The library is checked by sampling (three repositories so far); this guide was not individually reviewed. Verify against the project before relying on it. Unofficial; not affiliated with or reviewed by the project._
