DEV Community

Empellio.com
Empellio.com

Posted on • Originally published at Medium

Why We Wrote a Process Manager in Rust (and what surprised us)

We build developer tools at Empellio. PM2 was always just there — install it, forget it, it works.

Until we started asking: how much of the overhead is actually necessary?

That question turned into Oxmgr — a Rust-based PM2 alternative. Here's what we learned.

Why Rust and not Go?

Go would've been faster to write. But we wanted two things Go doesn't give for free:

  • Memory safety without a garbage collector
  • Predictable latency without GC pauses

For a process manager running 24/7 and keeping your services alive — that tradeoff mattered.

Surprise #1: Polling is the wrong mental model

Our first daemon used a tick-based loop. Every X milliseconds — check processes, update metrics, handle restarts.

The problem: if a process crashes 1ms after a tick, you wait almost the entire interval before reacting.

The fix? Let the OS tell you when something happens instead of constantly asking. When a child process exits, handle the event immediately.

No polling. No waiting. Just react.

Surprise #2: The benchmark numbers

We expected Rust to be faster. We did not expect this:

Crash detection:
oxmgr →  4ms
pm2   → 170ms

Difference: 42x
Enter fullscreen mode Exit fullscreen mode

Our first reaction was that we'd made a mistake. We ran it again. Same numbers.

The reason isn't Rust magic — it's PM2's Node.js IPC layer. When a process crashes, the signal travels through the event loop before PM2 reacts. Oxmgr listens to OS exit events directly.

Memory was even more surprising:

Daemon RSS at 100 processes:
oxmgr →   7MB
pm2   → 148MB
Enter fullscreen mode Exit fullscreen mode

PM2 is a Node.js app managing Node.js apps. Oxmgr is a small Rust binary.

Surprise #3: The compiler finds your bugs first

Process state sounds simple: running, stopped, crashed.

In practice it's not. What happens when you restart a process mid-crash? What if a reload is in progress when a health check fails?

In most languages you discover these edge cases in production. In Rust, the type system forces you to model every state transition explicitly. If you haven't handled a case — it won't compile.

We found more bugs during development than we would have in any other language. Not because we were careful. Because the compiler was careful for us.

Where it is now

Oxmgr is at v0.1.4. Linux, macOS, Windows. Manages Node.js, Python, Go, Rust — anything you can run from a command line.

npm install -g oxmgr
Enter fullscreen mode Exit fullscreen mode

Repo + benchmarks: github.com/Vladimir-Urik/OxMgr

People are already running it in production — would love to hear how it goes for you.

Top comments (0)