pboss logo pboss.docs
Menu

More

Benchmarks

pboss vs pm2 and pup under identical load — the same app, the same runtime, the same machine, the same load generator; only the process manager differs. Headline numbers from the benchmark repo, with the full reports linked.

pboss is measured against pm2 (and, for Deno apps, against pup) in a dedicated benchmark repo — github.com/Procboss/benchmarks — with three editions, one per runtime: a Bun HTTP server, a Node HTTP server, and a Deno HTTP server. The method is the same in all three: the same app file, the same runtime binary, the same machine, the same load profile — the only variable is which process manager supervises it. That isolates the overhead of managing, not the runtime’s raw HTTP performance.

Every measured run starts from a verified-clean state (app deleted, daemon killed, port free) — each number is a cold start of the whole manager stack, and that cold-start time is itself measured. Manager order alternates between runs so time-of-run bias cannot favor a column, a discarded warmup run precedes every measured run, and CPU and memory cover the whole process tree (daemon + managed app), sampled every 0.5s from /proc. Raw autocannon JSON, resource CSVs, and event logs for every number live under each edition’s results/raw/.

Both supervision models are covered: fork (one managed process) and cluster (N workers sharing one port). Crash-recovery and restart-storm runs are separate phases that never overlap the HTTP measurements.

Managing a Node server#

Node v24.21.0 · pm2 5.4.3 · pboss v1.8.0 · fork mode, 100 connections:

Metricpm2pbossWinner
Average requests/sec57,51257,519tie (+0.0%)
Average latency (mean)1.44 ms1.40 mspboss (-2.8%)
Average memory (tree)179.1 MB168.3 MBpboss (-6.0%)
Average cold startup524.6 ms396.6 mspboss (-24.4%)
Warm startup (median)270 ms174 mspboss (-35.6%)
SIGKILL crash recovery (mean)134.9 ms68.4 mspboss (-49.3%)
50-restart storm (median)376.0 ms181.0 mspboss (-51.9%)

Throughput is a tie because the manager is not in the request path — the supervision operational metrics are where the manager itself lives, and pboss wins every one of them. The idle daemon sits at 65.2 MB (pm2: 68.7 MB) at 0.0 % CPU on both.

Cluster mode — the apples-to-apples one, because for Node apps BOTH managers cluster natively through node:cluster: pboss --instances 2 spawns a supervised wrapper primary that forks the workers, pm2 -i 2 makes its daemon the primary; the worker count and sharing mechanism are identical.

Metricpm2pbossWinner
Average requests/sec55,95458,539pboss (+4.6%)
Average latency (mean)1.25 ms1.18 mspboss (-5.6%)
Cold startup (median)637 ms419 mspboss (-34.2%)
Warm startup (median)422 ms242 mspboss (-42.7%)
Worker respawn after SIGKILL (mean)151.1 ms93.2 mspboss (-38.3%)
Restart storm, all workers (median)498 ms300 mspboss (-39.8%)

The respawn finding, and the fix. The first run of this edition exposed a real defect: a SIGKILLed cluster worker took a 7.3 s median to be replaced — the old respawn policy treated every quick death as an unstable crash and escalated its backoff (300 ms doubling to a 15 s cap).

pboss v1.8.0 replaced that policy: a worker that lived is a healthy worker that died, and its replacement is forked immediately in the exit handler — backoff exists only for genuine boot-crash loops. The re-run measures 93 ms — 38.3 % faster than pm2’s 151 ms for the same event.

pm2 keeps an edge in cluster-mode memory: its tree is ~11 % smaller, because the pm2 daemon itself is the cluster primary while pboss runs a supervised wrapper process per app. Health stayed up through 100 % of worker kills on both sides.

Full report: benchmarks/node/results/REPORT.md (an HTML rendering and all raw data sit beside it in the repo).

Managing a Bun server#

Bun 1.3.14 · pm2 5.4.3 · pboss v1.8.0 · fork mode, 100 connections:

Metricpm2pbossWinner
Average requests/sec73,89272,139pm2 (-2.4%)
Average memory (tree)119.5 MB117.9 MBpboss (-1.3%)
Average cold startup389.2 ms338.2 mspboss (-13.1%)
Warm startup (median)192 ms117 mspboss (-39.1%)
SIGKILL crash recovery (mean)67.3 ms65.9 mspboss (-2.1%)
50-restart storm (median)255 ms129 mspboss (-49.4%)

pm2 keeps a marginal throughput lead at this load; pboss wins every operational metric — cold start 13 % faster, warm start 39 % faster, storm restarts half the time. The idle daemon costs 63.7 MB (pm2: 66.3 MB) at 0.0 % CPU on both.

Cluster mode for Bun apps: pm2 not supported. pm2’s cluster mode is node:cluster inside its Node daemon — it cannot fork Bun workers, so -i N --interpreter bun silently degrades to N plain fork processes, and forcing exec_mode: "cluster_mode" runs the workers under Node, not Bun.

The benchmark records pm2 as not supported in this mode and measures pboss alone: --instances 2 workers sharing one port through SO_REUSEPORT serve 73,204 req/s at the headline level, with a SIGKILLed worker replaced in 81.3 ms and the shared port never going dark; the whole cluster restarts in 178 ms.

Full report: benchmarks/bun/results/REPORT.md (HTML rendering and raw data beside it in the repo).

Managing a Deno server — vs pup#

Deno 2.9.7 · pup 1.0.4 · pboss v1.8.1 · 100 connections, 2 workers, both native cluster models (pboss: N SO_REUSEPORT workers · pup: built-in load balancer over N backends):

MetricpuppbossWinner
Average requests/sec (cluster)12,44469,088pboss (+455%)
Average latency, mean (cluster)7.51 ms0.96 mspboss (-87%)
Average CPU of tree (cluster)121.8%62.2%pboss (-49%)
Average memory of tree (cluster)269.4 MB158.2 MBpboss (-41%)
Cold startup, fork (median)1,142 ms316 mspboss (-72%)
Warm startup, fork (median)285 ms159 mspboss (-44%)
Idle overhead (RSS)88.3 MB63.3 MBpboss (-28%)
SIGKILL recovery, fork (mean)10,958 ms67 mspboss (-99.4%)
Worker respawn, cluster (mean)10,921 ms87 mspboss (-99.2%)
Health stayed up through kills70%100%pboss
Restart storm, fork (median)1,015 ms106 mspboss (-90%)

Deno fork-mode throughput is a statistical tie — 68,491 vs 67,953 rps, well inside run-to-run noise — with pboss 16 % lighter in tree memory and every operational metric (startup, restart, crash recovery, storm) ahead by 44–99 %. pup’s crash numbers reflect its default 10-second restart delay, run out-of-the-box exactly like the pm2 suites; a tuned restartDelayMs narrows the gap, but the default is what most installs run.

Full report: benchmarks/deno/results/REPORT.md (HTML rendering and raw data beside it in the repo).

How to read these numbers#

The winner column already accounts for direction — throughput higher is better, every other metric lower is better, and a level row reads “tie”. Numbers are medians over 5 runs per manager unless the row says mean, and the per-run lists are printed under every table in the full reports. The same-machine caveat applies: the load generator shares the CPUs with the servers, so absolute numbers are specific to that box — compare the two columns, don’t quote them as platform throughput.

Each edition pins its tool versions in the environment header (the reports carry date, OS, kernel, CPU, memory, runtime, and manager versions), so a regression re-run on a different machine still compares like with like within itself.

Run them yourself#

git clone https://github.com/Procboss/benchmarks
cd benchmarks/node   # or benchmarks/bun, benchmarks/deno
npm install
bash scripts/benchmark.sh

BENCH_PROFILE=smoke runs a fast pass first, BENCH_PROFILE=full adds a 1000-connection stress level, and BENCH_INSTANCES=4 sets the cluster worker count (default: CPU cores). The whole suite — HTTP levels, crash recovery, restart storms, idle overhead — is driven by one script per edition, and it regenerates the REPORT from the raw data it just measured.