On this page
One process per service
Every service is exactly one process: the unit's own process, with no helper, sidecar or child process beside it. The rule is the operator's, and Stado checks it as a property of the fleet rather than as a one-off cleanup. Two readings say a service breaks it, and either one is a violation.
What the host sees: child processes
On every beacon pass the host Stado process reads the kernel's process table
inside its own process (libproc on macOS, /proc on Linux; no ps child),
and for each declared unit with a pid it publishes the processes below that
pid as the unit's children in the beacon: pid, parent pid, the executable's
path and whether it has exited unreaped. A table the kernel will not list is
published as children_unread with the reason, never as no children.
stado service status <name> and stado service list print one line for a
unit that has any:
violation: one process per service is broken on example-host: com.wisent.forks runs 1 child process(es) under pid 4120: /bin/sleep (pid 4121); fold each spawned program into forks's own binary, then `stado service status com.wisent.forks` reads one process
--json carries the same reading as one_process_violation (host,
unit, service, pid, children, debt, detail), and a beacon whose
children cannot be read as one_process_error. A beacon older than its own
promised next publication describes no children.
What the catalog records: helper processes
The product catalog (catalog/products.yml) refuses a service declaration
that is not one process:
- a declared helper (
helpers,helper_processes,sidecars,children,launcher) is refused naming the field; - a
programthat is not the product's own binary — its id, or a binary its installation recipes name — is a launcher or interpreter that starts the service as a child, and is refused naming the file and why.
A service that cannot be fixed in the same change records what it still
spawns under service.one_process_debt: a reason, and spawns, each a
command and the site (<repository>/<path>:<line>) of the call that
starts it. A spawn without a source line is refused. The record is never an
exemption: stado product catalog, stado service catalog, stado service status, stado service list, stado doctor and Stado Desktop print every
record as a violation, naming each spawn site.
Doctor
stado doctor has a one-process row. It fails naming every unit that has
child processes in its host's latest beacon and every catalog service with a
debt record; it reads as unmeasured when the beacons' children could not be
read, and passes only when no unit is more than one process. It is not part of
--deployment-preflight: a service that is more than one process is fixed in
its own product and does not block installing an unrelated Stado release.
Stado Desktop
The Managed services table prints Not one process: … under a violating row,
its inspector carries a not one process badge and a panel with the same
sentence, and a product whose catalog service records debt shows the reason
and every spawn site in its inspector.
Fixing a violation
Fold the spawned program into the service's binary and release it. Film and
Obraz were started through a /bin/sh launcher that sourced the service
environment file and execed the binary; each binary now reads
FILM_SERVICE_ENV_FILE/OBRAZ_SERVICE_ENV_FILE itself and serves on the
assigned port when started with no command, so the release stages the binary
alone and names it as its launcher. Stado's host process held its power
assertions through caffeinate children (one per job, never reaped) and read
memory through sysctl and vm_stat; the power assertions are now IOKit
calls in the process, and hw.memsize and kern.memorystatus_level are
kernel calls (sysctlbyname), while the full memory reading still runs
sysctl -n vm.swapusage and vm_stat for swap and the compressor.
Real tests
kompilator test --manifest-path stado-rs/Cargo.toml --test one-process-catalog: a fixture service that runs its binary is accepted; one whose unit runs a launcher, one that declares a sidecar and a debt record without source lines are refused by the realstado product catalog; a recorded debt prints as a violation; the shipped catalog prints Brama's, Weles's and Stado's records.kompilator test --manifest-path stado-rs/Cargo.toml --test host-health -- one_process::(withSTADO_TEST_REQUEST_LIMITSdeclared): one isolated deployment and two init-system jobs of its own (launchd jobs on macOS, transient systemd user units on Linux), one/bin/shthat startssleepand onesleep. The beacon lists the child under the first only,stado service statusprints the violation for it and none for the other, andstado doctor --jsonfails itsone-processrow naming the first.
Source: this website