Benchmarks
Aug 21, 2026
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 is also the only one of the three that leaves nothing running after you stop a service, and the only one that neither duplicates nor loses your workload when the supervisor itself dies.
1.49
s
install to first supervised service
6.91
s
ten-service graph cold boot
12.4
MB
supervisor overhead at ten services
0
processes left behind on stop
Install
Each tool installed the way its users install it, timed until one service is actually supervised.
to first supervised service
sysg
1.49s
Supervisor
5.53s
Docker Compose
15.85s+
seconds, cold
pulled over the network
sysg
6.8 MB
Supervisor
48.0 MB
Docker Compose
172.5 MB
megabytes, cold cache
installed on disk
systemd
14.6 MB
sysg
19.5 MB
Supervisor
26.8 MB
Docker Compose
278.6 MB
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
Two independent roots, a five-wide fan-in. 0.64 walked the topological order one unit at a time; 0.65 dispatches every unit whose dependencies are already satisfied.
sysg 0.64.4 — one at a time
14.00s
13.83s
db
queue
cache
ingest1
ingest2
worker1
worker2
worker3
aggregator
reporter
0s
2s
4s
6s
8s
10s
12s
14s
10 steps
sysg 0.65.0 — by dependency level
14.00s
6.91s
db
queue
cache
ingest1
ingest2
worker1
worker2
worker3
aggregator
reporter
0s
2s
4s
6s
8s
10s
12s
14s
5 waves
Each bar is one service becoming ready. Playhead is wall-clock.
2.00×
faster than sysg 0.64.4
sysg 0.65.0
6.91s
Docker Compose
8.31s
1.20× slower
sysg 0.64.4
13.83s
2.00× slower
Supervisor
cannot express this graph
all four, one clock
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.
Readiness
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.
sysg
0.00s
health probe
Supervisor, tuned
0.00s
startsecs=5, matched to startup
Compose
−0.52s
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.
Resident cost
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.
sysg
12.4 MB
Supervisor
18.1 MB
Docker Compose
354 MB
area ∝ MB
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
The intercept matters less than the slope. sysg 0.66 adds an exec: argv form that runs a service directly instead of through a shell, which removes a resident process per service.
0
8
16
24
32
crosses at N≈395
↑
sysg exec:
29.5 MB
Supervisor
28.0 MB
sysg command:
84.7 MB
0
100
200
300
400
500
MB overhead
services supervised → · N=500
drawn to N=500
Fitted from N = 1, 10, 40. Drawn to N = 500.
added MB per service
Supervisor
0.020
sysg exec:
0.035
sysg command:
0.145
fitted slope
processes at N=40
sysg exec:
45
Supervisor
45
sysg command:
85
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.
Teardown
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.
sysg
none left
session teardown reaches the setsid child
Docker Compose
none left
container PID namespace collapses
Supervisor, tuned
2 left
stopasgroup + killasgroup; setsid child escaped the group
Supervisor, default
5 left
signals only the pid it spawned
stop →
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
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
kill -9 → restart
Docker tested at its default live-restore: false.
sysg
Supervisor
Compose
services survive
yes
yes
no
visible while down
yes
yes
no
no duplicate started
yes
no
yes
workload kept
yes
yes
no
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.
Summary
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
installs without root
systemd was measured on install size only; its runtime rows need a machine where it is PID 1.
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.
Method
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.