How systemg compares to other process managers

6 min read Original article ↗

to other process managers

We ran systemg through a gauntlet of tests against systemd, Supervisor, and Docker Compose — install, boot, memory, teardown, crash recovery, and readiness.

TL;DR: systemg installs faster than Supervisor, boots a dependency graph faster than Docker Compose, and uses less memory than either. It leaves nothing running after you stop a service — so does Compose, where Supervisor strands up to five — and it is the only one of the three that neither duplicates nor loses your workload when the supervisor itself dies.

install to first supervised service

ten-service graph cold boot

supervisor overhead at ten services

0

processes left behind on stop

Install

Getting to a running service

Each tool installed the way its users install it, timed until one service is actually supervised.

to first supervised service

seconds, cold

pulled over the network

megabytes, cold cache

installed on disk

megabytes

systemd ships with the distribution, so its marginal install cost is zero.

3.7×

faster to a supervised service than Supervisor

Supervisor's 48 MB is 47.6 MB of apt index plus 0.4 MB of packages — on a machine that ran apt today it pulls 0.4 MB. sysg pulls 6.8 MB in every cache state. systemd is 14.6 MB installed and ships with the distribution, so its marginal install cost is zero. 86% of Supervisor's disk figure is the CPython runtime; sysg carries its runtime compiled into the binary.

how this was measured →

Dependency graph

Ten services, five levels deep

Two independent roots, a five-wide fan-in. Every unit whose dependencies are already satisfied is dispatched at once, so the graph boots in five waves rather than ten steps — concurrency without violating a single edge.

Each bar is one service becoming ready. Playhead is wall-clock.

1.20×

faster than Docker Compose

Supervisor

cannot express this graph

Same graph, same clock. Each marker stops when its last service reports healthy.

Supervisor has no dependency edges. priority orders starts but does not gate on readiness, so this graph cannot be expressed. Compose runs each unit as a container and sysg runs each as a process — different isolation, and Compose ran against the host daemon while sysg ran in a container.

how this was measured →

Readiness

Reporting a service as up

A service that starts but cannot do work for five seconds. The marker is when the tool says it is up; the shaded band is the distance from there to when it actually works.

startsecs=5, matched to startup

probe lands on the next 1s tick

Supervisor, default

+3.88s

startsecs=1

Supervisor, 8s service

+2.93s

startsecs=5, startup varies

Band left of the marker is reporting late. Band right of it is reporting early.

startsecs is a fixed timer: set it to the real startup time and it is exact. A probe is closed-loop, so it does not need to be told.

how this was measured →

Resident cost

What the supervisor itself costs

Memory for the tool, minus the same services run bare. Ten near-idle services, so the tool dominates. These are orders of magnitude apart, so area is proportional to megabytes.

dockerd + containerd + one containerd-shim per container, measured inside the VM.

28×

less than Docker Compose, at ten services

On macOS, Docker Desktop adds a further 476 MB host-side for the VM and its helpers.

how this was measured →

Scaling

Cost per service, as services are added

The intercept matters less than the slope. sysg's exec: argv form runs a service directly instead of through a shell, which removes a resident process per service.

MB overhead

services supervised → · N=500

Fitted from N = 1, 10, 40. Drawn to N = 500.

added MB per service

fitted slope

processes at N=40

supervisor + children

Processes at 40 services. One per service under exec:, two under command:.

command: is unchanged and still spawns through a shell, which stays resident as the service's parent — 85 processes for 40 services instead of 45.

how this was measured →

Teardown

Stopping a service that forked children

A service and five descendants, one of which deliberately escapes the process group. Stop it with each tool's own command, then count what is still running.

session teardown reaches the setsid child

container PID namespace collapses

stopasgroup + killasgroup; setsid child escaped the group

Supervisor, default

5 left

signals only the pid it spawned

Filled squares are processes still alive after stop returned.

Survivors reparent to init and keep running. The loss is per stop/restart cycle, so a service that restarts hourly accumulates them.

how this was measured →

Control plane

When the supervisor dies

kill -9 the supervisor — dockerd for Compose, not the CLI — then start it again and look at what happened to the workload.

sysg

sysg (new pid)

└─ web pid 77

re-adopted, same pid

Supervisor

supervisord (new)

├─ web pid 77 orphan

└─ web pid 91 duplicate

duplicate started

Docker Compose

dockerd (manual)

└─ web exited

workload terminated

Docker tested at its default live-restore: false.

visible while down

yes

yes

no

no duplicate started

yes

no

yes

recovers unattended

yes

yes

no

Green is the outcome you want, whichever way the question is phrased.

Supervisor's services keep running, but supervisord has no record of them and starts a second copy. With live-restore: false the Docker daemon does not re-attach to containers from the previous session; live-restore: true is untested here. While dockerd was down the workload could not be inspected at all — every path goes through the daemon.

how this was measured →

Summary

The process manager for busy people

Numbers where a figure was measured, marks where the answer is yes or no. A dash means it was not measured for that tool.

sysg

systemd

Supervisor

Compose

install to first service

1.49s

5.53s

15.85s+

pulled over network

6.8 MB

48.0 MB

172.5 MB

installed on disk

19.5 MB

14.6 MB

26.8 MB

278.6 MB

ten-service graph

6.91s

n/a

8.31s

overhead at ten services

12.4 MB

18.1 MB

354 MB

added per service

0.035 MB

0.020 MB

7.26 MB

expresses a dependency graph

gates on a probe, not a timer

leaves nothing behind on stop

workload survives its crash

starts no duplicate after

recovers without an operator

runs without a separate runtime

systemd was measured on install size only; its runtime rows need a machine where it is PID 1.

how this was measured →

systemg is open source and always welcomes contributors and users. Bugs, benchmark disputes, and re-runs that disagree with anything above are all useful — every script that produced these figures is in the repo, so a contradicting result is a pull request rather than an argument.

The full writeup carries every trial, the pinned versions and images, the reasoning behind each metric, and the four harness bugs that produced plausible wrong numbers before they were caught.

Every tool gets the same service body and the same probe interval. Where a setting changes a result — stopasgroup, startsecs, exec: — both configurations are charted. Trial counts are 2–5 per figure, cold, with raw output published alongside the scripts.

Gaps: Compose ran against the host daemon while sysg ran in a container, so the boot figure is indicative rather than controlled. systemd is charted on install size only — its runtime rows need a VM. Docker's live-restore: true is untested. N=40 Compose memory is extrapolated from the measured per-container cost.

how this was measured →