s6 init
Runink River boots with s6, s6-rc and s6-linux-init from
skarnet.org. There is no systemd anywhere in the image:
no .service units and no systemctl. This is invariant 1 of the project and is not up for
negotiation in a normal pull request (FAQ: why not systemd?).
The pieces
| Program | Job |
|---|---|
s6-linux-init | PID 1: sets up the system, starts the supervision tree, and handles shutdown |
s6-svscan, s6-supervise | supervise every long-running service, restarting it if it dies; the scan directory is /run/service |
s6-rc | brings services up and down in dependency order, from a database compiled ahead of time (/etc/s6/rc/compiled) |
s6 (s6-frontend) | manages the enabled set and compiles the database (s6 set enable, s6 set commit) |
The image’s s6 packaging (the service stores /etc/s6/sv/ and /etc/s6/adminsv/, the
-s6 service packages for NetworkManager, OpenSSH, SDDM, BlueZ and CUPS) comes from Artix
Linux today, as part of the transitional base. In Runink River’s own-base scaffold, s6,
skalibs and execline already build bit-for-bit reproducibly from skarnet’s sources
(docs/OWN-BASE.md, section 5.5).
How the system comes up
- GRUB loads
linux-runinkand the initramfs from the EFI partition; the initramfs imports the ZFS pool, asks for its passphrase and mounts the boot environment. - s6-linux-init becomes PID 1 and starts the supervision tree.
- s6-rc brings up the default bundle, in dependency order. Runink River’s own oneshots
come first where it matters:
runink-fwloads the firewall before NetworkManager and sshd start,zfs-mountmounts/homebefore the filesystems are declared mounted and before SDDM starts, andriver-permsre-asserts file modes once they are mounted. udev, dbus, elogind, NetworkManager, Bluetooth, printing and the display manager follow. /etc/s6/rc.localruns last, in init context:sysctl --systemand zram swap sized to the machine.
The full dependency table is on Architecture.
A service is a directory
Services are directories, not unit files: a type file (oneshot or longrun), an up or
run script, and a dependencies.d/ directory with one empty file per dependency. The
firewall’s oneshot is three lines of execline:
#!/bin/execlineb -P
foreground { /usr/local/bin/runink-fw apply }
exit 0It always exits 0, because a failed firewall load must not keep the network and SSH down: the
machine has to stay reachable for repair. runink-fw logs what went wrong instead.
Everyday commands
s6-rc -a list # services that are up
sudo s6-rc -d change sddm-srv # stop a service (here: the login screen)
sudo s6-rc -u change sddm-srv # start it again
s6-svstat /run/service/NetworkManager-srv # state of a supervised longrun
s6-rc-db -c /etc/s6/rc/compiled contents default # what starts at bootMore, including how to add your own service: Services (s6).
The desktop on s6
KDE Plasma runs on s6 through elogind (the session and seat API that KWin, SDDM and
PowerDevil use) and dbus, on the non-systemd startplasma path. PipeWire, PipeWire-Pulse,
WirePlumber and the session bus run as user services. Two small pieces fill gaps that
systemd would otherwise cover:
- the
plymouth-quitoneshot ends the boot splash before SDDM starts, because SDDM has no Plymouth integration of its own and nothing else would stop it; - the installer writes
/etc/tmpfiles.d/runink-runtimedir.conf, which declares the first user’s runtime directory,/run/user/1000.
Anything the desktop does not need, and that must survive logouts, starts from rc.local in
init context, so elogind never reaps it.
Software that expects systemd
Packages that ship only a systemd unit need an s6 service definition instead. Tools that
require a running systemd (systemctl, systemd --user, journalctl) do not work. Before you
report a bug in a package, check whether it assumes systemd.
More: the boot sequence in docs/ARCHITECTURE.md.