bm2: A 5.5 MB MoonBit Process Manager That Runs Bun and Node.js with 1 ms Command Latency
A process manager that compiles to a 5.5 MB static binary with no runtime dependency cuts deployment surface area and memory overhead sharply compared to pm2's Node.js-based 23 MB install. For teams running Bun in production, bm2 is one of the first purpose-built managers that treats Bun as a first-class runtime alongside Node.
bm2 is a Linux process manager written in MoonBit, shipping as two native binaries totaling about 5.5 MB — roughly a quarter of pm2's size and with none of its Node.js runtime dependency. It manages Bun and Node.js applications through a per-user daemon controlled over a Unix socket, supporting multi-instance deployment, crash-auto-restart with a configurable budget, per-instance memory limits, graceful stop, and state persistence across daemon restarts.
A single `bm2.toml` file defines one project: runtime, script path, instance count, port range, memory cap, and restart policy. The CLI's `start` command always performs a full restart, so changing any config field takes effect immediately. Reserved environment variables — `BM2_APP_PORT`, `BM2_APP_INSTANCE`, and `NODE_ENV=production` — let each instance bind its own port and let the primary instance run migrations or cron jobs without extra coordination.
bm2 deliberately does not handle reverse proxying, load balancing, certificates, or hot reload. It leaves those to Nginx or Caddy, keeping its scope narrow: process hosting with built-in 10 MB × 10-generation log rotation, JSONL event streams for diagnostics, and a stale-socket recovery mechanism that auto-restarts a crashed daemon on the next CLI request.
Shipping a process manager as a native static binary eliminates the circular dependency of using a Node.js tool to manage Node.js processes — a single `bm2d` crash doesn't cascade through a runtime it itself depends on.
The 1 ms command latency versus pm2's 200–400 ms suggests the Unix-socket control path and MoonBit runtime avoid the event-loop serialization overhead that Node.js CLI tools pay on every invocation.
Deliberately scoping out reverse proxying, certificates, and hot reload keeps the codebase small and the failure domain narrow; the design bets that Nginx/Caddy already handle those concerns better than any process manager can.
Persisting instance state per project and fully deregistering killed projects means a daemon restart won't resurrect intentionally stopped apps — a common operational annoyance with pm2's dump/resurrect workflow.
The `min_uptime_ms` restart-budget gating prevents a flapping process from burning through its restart allowance on near-instant crashes, distinguishing genuine startup failures from runtime crashes.
The conversation barely engages with bm2 itself. Instead it drifts into runtime preferences: one person asks whether Bun or Deno is dominant, another dismisses Deno as impractical and sticks with Bun, and a third mentions switching to Bun 1.4 because of its Rust rewrite. The only remark touching the tool is a vague nod to a clear deployment boundary.
Are people all using Bun or Deno now?
I use Bun. Deno is a bit useless — I don't have a use for it.
The deployment boundary is clear.
[fist-and-palm salute]
Bun 1.4 was rewritten in Rust — I've already switched over.