# PM2's load balancer needs Node.js, its log rotation needs another package, and it is AGPL 3.0

> PM2 is a globally installed process manager for Node.js and Bun that daemonizes an app with one command. The built-in load balancer, the zero downtime reload, the startup script and the log rotation are four separate pieces with four separate prerequisites, and the only licence it states is AGPL 3.0.

**Unitech/pm2** — Node.js/Bun Production Process Manager with a built-in Load Balancer.

- Repository: https://github.com/Unitech/pm2
- Website: https://pm2.keymetrics.io/docs/usage/quick-start/
- Stars: 43,298 · Forks: 2,730
- Language: JavaScript
- License: NOASSERTION
- Published: 2026-08-17 · Updated: 2026-08-18 · Language: en
- Canonical page: https://hysenlabs.com/projects/unitech-pm2

## The built-in load balancer only exists in Node.js cluster mode

The load balancer in the description is not a general feature. It arrives with cluster mode, and cluster mode is offered for one runtime. `pm2 start api.js -i <processes>` is the only invocation the file gives for it, introduced as starting a Node.js application in cluster mode that leverages all CPUs available. `<processes>` can be `'max'`, `-1` for all cpu minus 1, or a number you choose. What gets distributed is stated as HTTP, TCP and UDP queries.

The general start section reaches wider than that. It lists `pm2 start app.js`, `pm2 start app.ts` and `pm2 start app.py`, and says you can start Node.js, Bun, Python, Ruby and binaries in $PATH. Those processes are daemonized, monitored and kept alive, but the file never extends the balancer to them.

So if your service is written in Bun, Python or Ruby, what you inherit from the daemon is supervision, not distribution. You end up balancing it yourself, and `-i` gives you nothing on those runtimes.

## pm2 reload all is called downtime-free with no preconditions attached

Zero downtime reload is one command, `pm2 reload all`. The text says hot reload allows you to update an application without any downtime, and separately credits cluster mode with faster socket re-balancing in case of unhandled errors. Neither statement carries a condition.

Nothing here says what a process must do to qualify. The file does not describe the signal the outgoing process receives, whether in flight requests are drained, how long the reload waits before it gives up on a worker, or what happens to a process still holding a long lived connection when the signal arrives. Those are exactly the cases where a zero downtime claim is either true or not.

The consequence is that you are trusting an unconditional guarantee. If a reload does drop traffic, the file gives you no way to separate a missing drain from your own socket handling or from a worker that exited before handing over.

## A Bun-only machine needs a root symlink at /usr/local/bin/node

There are two global installs, `npm install pm2 -g` and `bun install pm2 -g`, and they are not equivalent on a host that has only Bun. The reason is the shebang. The entry point begins with `#!/usr/bin/env node`, so with no node on the path the binary will not launch, and the fix offered is to create one:

```bash
$ sudo ln -s $(which bun) /usr/local/bin/node
```

Read what that asks of you. It runs as root, it writes into /usr/local/bin, and it gives the same Bun binary a second name so the shebang resolves to Bun's node-compatibility runtime. Install Node.js later and two things now want the name node, with nothing in the file saying which one wins or how to undo the symlink. A system wide change is made for a package level convenience, and the removal step is missing.

## Log rotation is a second package, not a flag

The log commands themselves are thorough. `pm2 logs` opens the stream, `pm2 logs APP-NAME` narrows it to one app, `pm2 logs --json` and `pm2 logs --format` change the rendering, and `pm2 flush` and `pm2 reloadLogs` clear and reopen the streams. Four output modes are named, standard, raw, JSON and formatted.

Rotation is the part that is not in the core. The text introduces it as enabling log rotation and points at a module:

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

That is a separate package with its own install step, and nothing in pm2 itself warns when logs grow without it. A deployment that follows the quick start and nothing else ends up with an unbounded set of log files, and it discovers that when the disk fills rather than when the rotation is needed.

## Surviving a reboot takes two commands, and only eight init systems

Persistence is laid out in three steps:

```bash
# Generate Startup Script
$ pm2 startup

# Freeze your process list across server restart
$ pm2 save

# Remove Startup Script
$ pm2 unstartup
```

The first installs the hook, the second freezes the list, the third takes it back out. They are not alternatives. A host with the hook but no frozen list comes back with a daemon and nothing inside it, and a host with a frozen list but no hook never reads it. The file does not mark the pairing as required, so the usual surprise is doing one of the two and assuming both happened.

Coverage is bounded as well. Eight init systems are named, systemd, upstart, systemv, openrc, launchd, rcd, rcd-openbsd and smf. Anything outside that list is unaddressed, and `pm2 startup` on an unlisted init has no described result.

## pm2-runtime replaces your container entrypoint with itself

The container path is two lines of Dockerfile:

```dockerfile
RUN npm install pm2 -g
CMD [ "pm2-runtime", "npm", "--", "start" ]
```

`pm2-runtime` is described as a drop-in replacement for `node`, which means the thing receiving signals at PID 1 is no longer node, it is pm2-runtime, with npm invoked through it. That is a real change to how a container stops, and it is easy to miss because the install line above it looks ordinary.

The file does not say what pm2-runtime does with a stop signal, how long it waits for workers, whether it exits non-zero when a worker refuses to end, or how a process list survives a container restart. An orchestrator with a fixed grace period meets that shutdown behaviour without anyone having read it, and the only description available here is the phrase drop-in.

## AGPL 3.0 in the text, no licence in the metadata

The licence paragraph states that PM2 is made available under the terms of the GNU Affero General Public License 3.0, and that other licences are arranged by emailing contact@keymetrics.io. The repository carries both a LICENSE file and GNU-AGPL-3.0.txt at the top level, so the intent is not in doubt.

The metadata tells a different story. The licence field on the repository record reads NOASSERTION rather than AGPL-3.0. Any tool that settles a project's licence from that field, which covers most package scanners and a good share of internal policy checks, returns no answer instead of a network copyleft answer.

So the compliance question has two outcomes depending on where you look. Scanning the repository gets silence, reading the file gets AGPL 3.0, and the only documented route to anything else is an email, with no price or terms attached to it.

## The examples directory is where the real documentation lives

The file covers single file starts. The repository does not stop there. Under examples/ sit close to thirty directories, and one of them is examples/ecosystem-file/, the configuration file route for describing several processes in one place. Nothing in the file explains it, so a multi service deployment has to find it by browsing the tree.

The tree is also the only map of what the project actually does. Directories such as examples/cluster-http/, examples/cluster-tcp/, examples/run-php-python-ruby-bash/, examples/start-a-binary/, examples/expose-custom-metrics/, examples/expose-triggerable-functions/, examples/send-msg/ and examples/signal-catching/ each answer a question the file leaves open.

Running any of it from a checkout means bash. Every script in package.json is a shell call, with test:unit as `bash test/unit.sh`, test:e2e as `bash test/e2e.sh`, test:parallel as `bash test/docker-parallel.sh` and test:windows as `bash test/windows.sh`. Since Windows is named as a supported platform, checking that path from source needs a POSIX shell, and `npm test` runs the unit and end to end suites in series.

## Conclusion

Pick PM2 if you want one global binary that keeps a Node.js service alive, reloads it, and balances HTTP, TCP and UDP across cores, and if you are willing to install pm2-logrotate and run both startup and save yourself. Pass on it if your workload is Bun, Python or Ruby, because the built-in load balancer is offered only for Node.js cluster mode, or if AGPL 3.0 is a problem, because the file routes every other licence through an email to keymetrics.io with no price attached. Before adopting it, check how pm2-runtime handles a container stop, and check that your platform has the shell its test scripts need.

## FAQ

### How much does PM2 cost?

PM2 is made available under the terms of the GNU Affero General Public License 3.0, and for other licences you contact keymetrics.io. No price is listed anywhere in the file.

### What is PM2 and why is it used?

It is a production process manager for Node.js and Bun applications with a built-in load balancer. The stated reasons are keeping applications alive forever, reloading them without downtime, and making common system admin tasks easier.

### What is the latest version of PM2?

package.json declares 7.0.4, and the release list shows v7.0.4 dated 2026-08-24, with v7.0.3 and v7.0.2 both dated 2026-06-29. To move to the newest published build the file gives `npm install pm2@latest -g` followed by `pm2 update`.

### How do I set up PM2?

Install it globally with `npm install pm2 -g` or `bun install pm2 -g`, start an app with `pm2 start app.js`, then generate a startup script with `pm2 startup` and freeze the list with `pm2 save`. Node.js 18+ or Bun 1+ is required, and Linux, macOS and Windows are named as supported.

## Sources

- [Official documentation](https://pm2.keymetrics.io/docs/usage/quick-start/)
- [Official README](https://github.com/Unitech/pm2#readme)
- [Project repository](https://github.com/Unitech/pm2)
- [Release notes](https://github.com/Unitech/pm2/releases)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/unitech-pm2
