Comparison

PM2 vs systemd vs Bernato for AI Agent Fleets

Akshay Sarode 9 min read
Direct answer

PM2 and systemd are excellent at their actual job: start a process, restart it if it dies, capture stdout. Neither one gives you a live terminal into what an agent is doing right now, a way to approve a risky action from your phone, per-agent filesystem and network sandboxing, or a single view across every machine you own. Bernato is built for that specific layer, running on top of (not instead of) whatever already restarts your other services.

I run four or five coding agents on a given day, spread across a Mac Studio in the closet and a laptop that travels with me. The first time one of them decided to touch a build directory it had no business touching, I was on a subway with no way to see what it was doing, let alone stop it. That's the moment PM2 and systemd stopped being the right tool for this particular job. Not because they're bad process managers, they aren't. Because "keep this process alive" and "supervise a fleet of agents that can read your filesystem and hit the network on their own judgment" are different problems that happen to both start with a long-running process.

What PM2 and systemd are actually built for

Give credit where it's due. systemd is the init system on nearly every Linux box you'll touch, and it does its job well: dependency ordering, socket activation, cgroup-based resource limits, structured logs in the journal, and a restart policy (Restart=always, bounded by a StartLimitBurst window) that has been battle-tested across two decades of production services. PM2 solved a narrower but real problem: Node processes crash on unhandled exceptions more often than most teams like to admit, and pm2 start app.js plus cluster mode gets you restart-on-crash and multi-core usage without hand-writing a systemd unit file.

Both tools answer the same underlying question: is my process running, and if not, why did it stop and can it come back automatically. For a web server, a cron-adjacent worker, or a Discord bot, that question covers most of the job. Neither tool assumes the process it supervises might decide, mid-run, to read a file it wasn't supposed to, open a connection nobody expected, or need a human's explicit go-ahead before doing something risky. That assumption is exactly the situation you're in with coding agents.

Where the agent-fleet workload is different

A coding agent isn't a static binary that does the same thing every time it restarts. It reads context, decides what to do next, and acts, sometimes touching files outside the directory you meant, sometimes shelling out to a command you didn't anticipate. Supervising that well means answering questions PM2 and systemd were never asked to answer:

PM2 and systemd have no opinion on any of these. That's not a knock on either tool, it's simply outside the scope of "start it, restart it, log it."

PM2 Keeps a Node process alive systemd Keeps any Linux service alive Bernato Supervises, sandboxes, proves agent runs
All three keep a process alive. Only one of them was built around the agent needing to be watched, approved, and sandboxed while it runs.

Live remote terminal and mobile approval

pm2 logs and journalctl -f both give you a stream of stdout. That's useful after the fact, and not much help the moment an agent is halfway through editing a file and you need to see the actual state of its terminal, not just what it already printed. Bernato's dashboard, on web or in the mobile app, gives you a live PTY: the same interactive terminal session the agent is running in, viewable and controllable from a browser or your phone. You're not tailing a log. You're looking at the terminal.

The approval piece is the bigger gap. Neither PM2 nor systemd has any concept of an in-progress action that pauses and waits for a yes or no. Bernato does: an agent about to take a privileged action can surface that request, and you can approve or deny it from your phone before it happens. That single feature is the difference between finding out about a bad command after it ran and stopping it before it does anything.

Per-agent sandboxing versus "runs as your user"

Start something with PM2 or systemd and it inherits whatever permissions the account running it has. Neither tool has a per-process concept of "this agent can write to its workspace but not read your SSH keys" built in by default. systemd technically ships sandboxing directives (ProtectSystem, PrivateNetwork, and a handful of others) but they're opt-in, unit-by-unit, and in practice almost nobody hand-writes a full set of them for every agent process they spin up.

Bernato issues capability tokens per agent: workspace_rw, network, subprocess, screen_capture, accessibility, and full_disk_access. Each one is enforced at the OS level (macOS Seatbelt today), not just checked somewhere in application code. An agent scoped to workspace_rw only can't reach the network even if its own logic tries to, because the sandbox refuses the syscall before it completes.

Cross-machine fleet view

pm2 list and systemctl status are both local to the box you're on. Run agents on three machines and that's three separate terminal sessions and three separate mental models of what's actually happening. Bernato's daemon, bernatod, dials out over a WebSocket broker, so no machine needs an inbound port open, and the dashboard gives you fleet supervision across every machine you own from one place. Pairing a new machine uses a one-time capability token instead of exchanging SSH keys, so adding a laptop to the fleet is a few seconds of setup rather than a key-management chore.

When you're on the same Wi-Fi as a machine, the dashboard talks straight to it over LAN, direct to 127.0.0.1, and only falls back to the tunnel when you're off-network. That matters for how the terminal feels: local traffic doesn't carry the same round-trip as routing every keystroke through a broker.

Audit trail

The journal and PM2's stdout capture both tell you what a process printed. Neither is built to answer "what privileged actions did this agent actually take, and can I prove that record wasn't altered." Bernato keeps a local activity log of every privileged action today, and a hash-chained audit log with hourly verification is on the roadmap. That's the difference between a log you hope is complete and one you can eventually verify.

Feature by feature

Filter the table below by what you actually care about right now.

Filter by what matters to you
CapabilityPM2systemdBernato
Auto-restartYes. max_restarts, min_uptime, backoff options.Yes. Restart=always plus a StartLimitBurst window.Yes. Restart-on-crash with a ceiling and backoff so a bad agent can't loop forever.
Live remote terminalNo. pm2 logs streams stdout, it isn't an interactive session.No. journalctl -f tails the journal, same limitation.Yes. Live PTY, viewable and controllable from a browser or phone.
Mobile approvalNo concept of it.No concept of it.Yes. Approve or deny a risky action from your phone before it happens.
Per-agent sandboxingNo. Runs with whatever permissions your user account has.Partial. Sandboxing directives exist but are opt-in, per unit file.Yes. Capability tokens per agent, enforced by the OS sandbox (macOS Seatbelt today).
Cross-machine fleet viewNo. pm2 list is local to the box you're on.No. systemctl status is local to the box you're SSHed into.Yes. Fleet supervision across every machine you own, from one dashboard.
Audit logNo. stdout capture only.Partial. The journal logs output, not privileged-action attribution.Yes. Local activity log today; hash-chained log with hourly verification on the roadmap.
Setup complexitypm2 start app.js. A couple minutes.Write a unit file. Minutes to an hour depending on which directives you use.Pair a machine with a one-time capability token, no SSH keys. A couple minutes.
6
Capability token types per agent
0
Inbound ports required, dial-out broker
1
One-time token to pair a machine, no SSH keys

None of this means retiring systemd or PM2. bernatod is itself just a process, and on a Linux box it's entirely reasonable to let systemd keep bernatod running and restart it if it crashes, the same as any other daemon. What changes is what's supervising the coding agents specifically: that job moves to Bernato, because it's the layer that knows what a capability token is and can put a terminal in your pocket.

FAQ

Can I run PM2 or systemd alongside Bernato?

Yes, they solve different layers. Let systemd or PM2 keep whatever they're already responsible for running, including bernatod itself on a Linux box. Bernato supervises the coding agent processes specifically: sandboxing, the live terminal, mobile approvals, the fleet view. There's no conflict, because they aren't managing the same thing.

Does Bernato replace systemd on a Linux server?

No. Bernato supervises agent processes specifically, it isn't a general init system. It doesn't do dependency ordering between arbitrary services, socket activation, or boot sequencing. If you need something to bring up your whole system in the right order, that's still systemd's job.

What's the resource overhead of the Bernato daemon?

bernatod is a single compiled Go binary, with no embedded browser or per-agent runtime bundled into the daemon itself, so its own footprint is small relative to whatever the agent process you're running actually needs. We haven't published formal benchmark numbers for this post, so treat that as a qualitative point, not a specific figure: budget your resources around the agent workloads, not the daemon.

Is per-agent sandboxing available on Linux, or only macOS?

The capability token model (workspace_rw, network, subprocess, screen_capture, accessibility, full_disk_access) is the same everywhere you run Bernato. Today's OS-level enforcement mechanism is macOS Seatbelt specifically, that's what's actually shipping right now.

Do I need SSH keys to add a machine to the fleet?

No. Pairing a machine uses a one-time capability token instead of exchanging SSH keys.