[public testing encouraged] Deployment of s6-linux-init and s6-rc on your system

This guide aims to encompass s6 installation on multiple distros.

What's "s6" you might ask?

It stands for “skarnet.org's small and secure supervision software suite”.

Its init, named s6-linux-init component functions in a similar fashion to daemon-tools and runit, i.e. it starts processes whenever they crash, exit, or are killed, unless you have a “down” file in the service directory (which happens only when you disable the service or it wasn’t enabled to begin with, or at startup when its dependency service is not started yet).

The s6-rc is where magic happens. This is more akin to OpenRC, Upstart and systemd, where a dependency graph is created for all programs, based on user input / configuration. Everything is user configurable, but package maintainers should take care of most of the dependencies for you. User services will typically land in a “multi-user” bundle (basically a runlevel on steroids), where you can define additional dependencies for each service.

You can customize your own service as easily as making a new directory with a custom name (that doesn’t conflict with the original, so package updates from the repo don’t overwrite your changes), copying all the old files in here and modifying them to your needs and finally touch a file with the same name as your custom service to the bundle name. In 4 short commands, you got your custom service definition.

Scope of this wiki.

A separate wiki entry is maintained for how to use s6 without a frontend like 66. This is where most of the explanation on how s6 works and how to use it will go. [WIP needs a lot of updating] This wiki is instead aimed at how to install s6 and the custom “runlevel” layout that I’m hoping will become a standard.
Easy to follow Beginner Guide on s6 Starter Pack

The initial focus for now is on Void, Alpine and Devuan, with the later two coming after a while. Here is where you can help improve this wiki. If you want to test the guide and the services on other distros and you find something’s not working and find a way to fix it it, edit this wiki or add a comment.

Distros might have custom binary names (like cronie, crond, cronie-crond etc.), so services might not easily translate from one distro to another. However, this wiki aims to standardize the “runlevels” (for lack of a better term, as s6 doesn’t work with runlevels, but “bundles,” which are more flexible) and to make it easy for a distro to just copy the service file, slightly modify the run file and be off to the races.

Part of this wiki will end up on github, to make it easy to clone the services and make distro specific branches of services (master branch = generic, void branch, alpine branch, devuan branch, gentoo branch etc).

This method is highly volatile, dealing with “make install” and subject to changes. [WIP] on xbps-src packages, but this method will remain part of the wiki for a generic, distro-agnostic guide (which will contain explanations more than commands).

Artistic considerations. Explaining how the custom startup layout works.

Explanations on s6-linux-init are given in comments in the deploy script, which will turn into the generic script [WIP] at some point. The focus is on s6-rc, where you’ll be spending most of your time (which isn’t a lot, unless you’re going to be one of the maintainers or sysadmins writing the service files).

The /etc/s6-rc folders have 4 top-level folders (aside from compiled stuff you’ll make):

  • config
  • s6sv
  • scripts
  • source

config

The “config” folder contains configurations used by the services, mostly environment variables, which counts as log lines for loggers, or custom flags for services (like if you want to enable more verbosity without recompiling the s6-rc-db, but by just restarting the service outside of s6-rc, in s6-svscan - check the s6 starter pack wiki if this sounds confusing).

scripts

This is intentionally addressed early, because it’s not much to mention. The scripts folder is the place where scripts are ran outside the actual service. The s6-rc services, particularly oneshots, should be very small scripts written in execline. If you need to call a huge shell script, it’s best to make a small execline oneshot that calls your huge script from /etc/s6-rc/scripts.

This way, if you modify your script, you don’t have to recompile the s6-rc db, just restart the service and it’ll execute the modified script with the same name and location.

There’s a script [WIP] that reapplies all the dependencies for all the services, to make sure that nothing tries to start early and everything has at least the dependency above them. At some point this will probably be automated with a daemon.
:man_shrugging:

Another [WIP] is automating the enablement of services when new things pop up in the sources folder (so users don’t have to touch).

s6sv

The “s6sv” is more like an “all services” folder, either enabled or disabled. This is where everything packaged by repo maintainers should go. The folders structure underneath s6sv mirrors the one in “source” for the reason that, to enable services a user should only have to add them to “source” and touch a file with the name of the folder in the bundle contents.d folder (coming to this in a bit).

Sysadmins could add service folders in either s6sv or source folders. If it’s in s6sv, make sure they’re not the same name as what comes from the repo, otherwise it’ll get overwritten with updates.

What I recommend is to name your custom service folders with with the suffix “-custom” or “-$(hostname).” If you manage more than one host, -custom would be the generic service that goes everywhere, whilst a host’s name as the suffix means the service would be custom for that host alone. You may create your own naming scheme (like add a suffix based on role, like “sshd-databases” or something, that goes on specific servers you want - the sky’s not the limit).

As far as custom / default services that go in the source directory, it doesn’t matter, because it’s going to be either symbolic links to the s6sv folders, or a custom folder. Bonus points: if you put your -custom service in s6sv, you can do a symlink in “source” with the same name as the repo provider service (/etc/s6-rc/source/20-multi-user/dhcpcd → /etc/s6-rc/s6sv/20-multi-user/dhcpcd-custom), to make a service easier to manage everywhere (you literally invoke “s6-rc -u change dhcpcd” instead of using the “dhcpcd-custom” name).

source

The “source” directory is the most important, as it’s the “source of truth” on the system. While it’s relatively easy to analyze a compiled s6-rc db with the command aptly named s6-rc-db, the command doesn’t show you the bundle names when doing dependencies, making it a bit harder to “decipher” (although not impossible).

Here, I go with the philosophy that source doesn’t need to be tracked. At most, an advanced user would either user git to track the changes in source, or would have a source-initX associated with each s6-rc db compiled-initX database. One could literally copy the “source” folder to the compiled-initX that was compiled from that source folder (s6-rc won’t care about it). That way, you know what services you have defined where.

That’s optional and up to the user. Just keep in mind that if you want to see how your system changed, if you don’t keep a copy of the “source” associated with the s6-rc compiled db, after many changes, you won’t know what went wrong with the current source folder. This is generally fine, because, by this design, users shouldn’t be able to break their system (the core services that makes the OS tick should still come up no matter what).

The folder structure of “source” contains the following:

  • 00-default
  • 01-ok-all
  • 05-ok-init
  • 10-ok-local
  • 20-ok-multi-user

The folders have names in them just to make it easy for users and admins to understand the dependency tree of the system. Think of these folders like you would of runlevels on steroids.

Services under 20 depend on 10, 10 depends on 5, 5 depends on 1, 1 depends on 00 and 0 has no dependencies. This makes it extremely important that the lower-level stuff works. Ideally, users won’t modify anything bellow 20, but at most, users should only work with 10 and above. For extremely experienced users, the symlinks from 05 and lower can be removed and replaced with their preferred services. The lower-level stuff should ideally be reserved for distro maintainers and should rarely be touched.

Dependencies would look something like this:

Note: I’m leaving side dependencies, because they’re hard to “draw,” e.g. mount-filesystems would have dependencies on udev-settle, modules and mount-tmp

00
   \-- ok-init
          \-- ok-local
                 \-- ok-multi-user

ok-init
   \-- init-dev
        \-- init-{btrfs,zfs,dm-crypt,dmraid}
   \-- init-proc
         \-- init-modules-load
   \-- init-sys
         \-- init-sysctl
               \-- init-static-devnodes
                      \-- modules
                      \-- udevd
                            \-- udev-settle
                                    \-- mount-filesystems
                                            \-- mount-rw

ok-local
   \-- net-lo
   \-- rc-local
          \-- multi-user (this is just a welcome to multi-user-land, like 00)

ok-multi-user
   \-- acpid-log
         \-- acpid
   \-- sshd
   \-- dhcpcd
          \-- iscsid-log
                \-- iscsid
                       \-- iscsi-login
                               \-- libvirtd
                                    \-- libvirtd-vm001
                                    \-- libvirtd-vm002
                                    \-- libvirtd-vm003
                                    \-- libvirtd-vm004
                                    \-- libvirtd-vm005
   \-- cronie
   \-- chronyd

You can kinda see how services in ok-multi-user are becoming slightly more parallel, like acpid, sshd (unless you need dhcp, but mine only needs rc.local to run, since that’s where static networking gets enabled), cronie, chronyd and more. Only very few would have crazy dependencies like libvirtd needing iscsi in order to start VMs on system start-up.

Conversely, you can see how lower-level stuff is more sequential. Traditionally, distros would write shell scripts to take care of the early init stuff, before launching PID 1. And some, like void, would have to run shutdown scripts (runit stage3), where after the runit process has killed everything, the system cleanup process starts.

This is not necessary with s6, since everything is handled in reverse-order on shutdown, using either friendly kill signals for longruns, or using the down scripts for oneshots (if applicable). There is however a “rc.shutdown.final” stage, where things can be done after the services are stopped and the file systems have been unmounted. You could literally mkfs your rootfs or zfs-rollback if you’d be using a stateless system (although this particular scenario is generally better performed at boot, since it’s possible you’ll have a forced poweroff that won’t clean your FS). The rc.shutdown.final probably has its own uses, I just haven’t found them yet.

The numeric value won’t be present in s6-rc’s db, this is just for users to make some sense of the dependencies and to make it easy to automate application of dependencies. In s6-rc, you’ll only see the names after the number prefix.

The reason this is not in order, is to give users the ability to add custom “runlevels” in-between what’s currently predefined. For example, Laurent Bercot has 2 more bundles in his examples, called “ok-lan” and “ok-wan.” These aren’t used here, but could be applies by an admin running a router to trigger services.

default

This is the main OS bundle. When the OS starts, unless the bootloader’s cmdline is modified, your bootloader should always be calling on “default” variable (this is true for runit, openrc, s6 and I believe sysvinit and systemd too). Their inits are the ones that call on “default.”

Inside it, you’ll find a service called “00,” which is just an informal service, just outputs a welcome on the screen (notifies you “init stage2” is happening, i.e. you just moved past s6-svscan, a.k.a. PID 1 is active).

Another thing you’ll find in default is the “ok-all” bundle, which is another bundle. This doesn’t need to be modified, assuming users are using the already existing “top-level” bundles on the system (the numeric folders).

ok-all

This contains all the other “ok-” bundles. This is more of a management thing, to make it easy to find what services are going to actually be activated. ok-all is not a runlevel in itself, it’s literally meant to start all the oks in parallel. The ok-all bundle could literally be renamed “enabled” and things would start to make sense.

Under the 01-ok-all, you’ll find the other oks. All oks are bundles. Under each, you’ll find the “contents.d” folder, which shows you what and when things will get activated. Keep in mind that the dependency happens at the service-level, not on a bundle (I discussed this shortly with skarnet, it’s intentional to avoid some weird edge-cases - and it’s also been discussed during the inception of s6).

ok-init

This is where the real OS initialization / userland begins. This is where udev is activated, kernel modules loaded, urandom activated, file systems are mounted, TTYs enabled and so on. You really shouldn’t be touching this.

The idea with the 05-ok-init folder is to basically “for each file in 05-ok-init/*, touch the file in 01-ok-all/ok-init/contents.d/” - it’s all meant to make the logic of activating services easy.

Similarly with the folder structure, all the items under 05-ok-init need to be dependent on 00 (the stage2 startup, that welcome message basically). So, the logic is “for each [ longrun or oneshot ] in 05-ok-init, touch 05-ok-init/service/dependencies.d/00” - this makes all the services in ok-init bundle depend on the 00 service.

Atomic services in ok-init can also have dependencies on one-another (e.g. local disk mount-rw depends on kmods being loaded, which in turn depends on udev being enabled, which in turn depends on dev, proc and sys fs to be mounted).

One important aspect of ok-init and the only time where you can apply a setting on a bundle that gets applied to everything in the bundle, is the “flag-essential” file. ok-init has this flag-essential, which basically means these services won’t go down with just an “s6-rc -d,” but you need to utilize the harder “s6-rc -D” command (this is invoked by the shutdown command, under s6-linux-init/current/scripts.rc.shutdown).

If all services are set to restart, these won’t be impacted. Also, most of ok-init atomic-services are oneshots that don’t have a “down” service (e.g. a remount root rw won’t have a umount root service file - for 1, because s6 handles unmounting of fs by itself and 2, because services with no “down” services are considered they’ll never go down anyway; once activated, the system state is in a good condition to allow other services to start and the state won’t change in the service).

10-ok-local

This is a very short bundle with “pre-mutli-user services.” If you need to mount custom file systems that aren’t handled by fstab (idk, autofs or something?), here’s where you’d be adding things. Currently there’s only a loopback address activation and the rc-local service, which calls on /etc/rc.local file (remnant from void’s runit). The script can be edited to also activate more custom scripts from “/etc/local.d/.start" and made to deactivate by "/etc/local.d/.stop.” Everything in “down” is commented out, thus the “down” script is considered empty.

20-ok-multi-user

This is where most userland begins. DHCP gets activated, SSHD (which depends on DHCPCD or not) starts, so does a cron daemon and an NTP daemon. This is the place most users will be spending time in, by linking services from /etc/s6-rc/s6sv/20-ok-multi-user to /etc/s6-rc/source/20-ok-multi-user.

Just like before, services in the ok-multi-user bundle can have dependencies amongst themselves. For example, sshd

Particularly aesthetic decisions:

The sshd, cronie and chronyd services are not enabled by default. That’s because Void doesn’t enable sshd by default and the NTP and cron daemons are not installed by default, but are left to the user’s choice.

As a homework for you, the first things you’ll want to do once you deploy s6 is to symlink those services to their respective sources locations and touch the file names under the 01-ok-all/ok-multi-user/contents.d folder.

Note: “sshd” is a bundle which includes “openssh-server” and “openssh-server-log.” That means you only need to touch /etc/s6-rc/source/01-ok-all/ok-multi-user/contents.d/sshd to make both of them to get enabled (after an s6-rc db recompilation, of course).

Before you being

The default source doesn’t contain sshd, openssh-server and openssh-server-log services (1 bundle and 2 longruns). You also lack things like an ntp client or cron daemon.

As a homework, after you’re finished (maybe before the final reboot step), you are to symlink the 20-multi-user folders with sshd, openssh-server and openssh-server-log (or a different ssh server and its respective service - and make a custom sshd bundle-type service under “source” named “sshd” and include your preferred ssh daemon and its logger).

Void install
#!/bin/sh

sudo xbps-install -S git make gcc 
echo ignorepkg=runit | sudo tee -a /etc/xbps.d/99-runit.conf > /dev/null
echo ignorepkg=runit-void | sudo tee -a /etc/xbps.d/99-runit.conf > /dev/null

# At the time of writing this script, there's a bug in s6 when shutting down where it says something about "execlineb matching } not found in ./stage4" - to ensure you're not building a broken package of anything, I've hard-coded my known-to-work git commits to compile s6 from.

git clone git://git.skarnet.org/skalibs
cd skalibs
git reset --hard 0e86d31f6091903510fc5e6be2d0dea6ff54b1c8
./configure && make && sudo make install

cd ..
git clone git://git.skarnet.org/execline
cd execline
git reset --hard 723e6638b81408b44a0a8450a84084a9e514898e
./configure && make && sudo make install

cd ..
git clone git://git.skarnet.org/s6 
cd s6
git reset --hard 9759bfb1fa9c8e7d27678c8405607d5a444e6941
./configure && make && sudo make install

cd ..
git clone git://git.skarnet.org/s6-linux-utils
cd s6-linux-utils
git reset --hard 4ac235b523cd1b50bd681e31c8241a2b8bdd3060
./configure && make && sudo make install

cd ..
git clone git://git.skarnet.org/s6-portable-utils
cd s6-portable-utils
git reset --hard 00ad09ecf7ad44e32c3cc54819bbf50e944fd4f9
./configure && make && sudo make install

cd ..
git clone git://git.skarnet.org/s6-rc 
cd s6-rc
git reset --hard b7634d9e7946092f9fecd33dbbd89b86b5701bed
./configure && make && sudo make install

cd ..
[ -e /etc/s6-linux-init ] && sudo rm -rf /etc/s6-linux-init
git clone git://git.skarnet.org/s6-linux-init 
cd s6-linux-init
git reset --hard 28787fc0512f8df17809c1a0507da2bfed552082
./configure && make && sudo make install

cd ..
[ -e /etc/s6-rc ] && sudo rm -rf /etc/s6-rc
git clone https://github.com/BikyAlex/s6_services.git
cd s6_services
sudo mkdir /etc/s6-rc
sudo cp -ar config /etc/s6-rc/
sudo cp -ar s6sv /etc/s6-rc/
sudo cp -ar scripts /etc/s6-rc/
sudo cp -ar source /etc/s6-rc/
sudo find /etc/s6-rc -type d -exec chmod 755 {} \;
sudo find /etc/s6-rc -type f -exec chmod 644 {} \;
sudo chown -R root:root /etc/s6-rc


cd ..
sudo mv /sbin/init /sbin/init-void
sudo mv /sbin/shutdown /sbin/shutdown-void
sudo mv /sbin/halt /sbin/halt-void
sudo rm -f /sbin/poweroff
sudo rm -f /sbin/reboot
sudo s6-ln -snf halt-void /sbin/poweroff-void
sudo s6-ln -snf halt-void /sbin/reboot-void


# If your linux kernel's customized, check if devtmpfs is mounted by the kernel, if not, add "-d /dev" to s6-linux-init-maker
# grep devtmpfs /proc/mounts && s6-linux-init-maker -1 -G "/sbin/getty 38400 tty2" /tmp/blah || s6-linux-init-maker -1 -d /dev -G "/sbin/getty 38400 tty2" /tmp/blah

sudo cp -ar /etc/s6-linux-init/skel /etc/s6-linux-init/skel.bkp

sed 's/# s6-rc-init/s6-rc-init/' /etc/s6-linux-init/skel/rc.init | sed 's/# exec \/etc\/s6-linux-init\/current/exec \/etc\/s6-linux-init\/current/' | tee /tmp/rc.init.tmp > /dev/null
cat /tmp/rc.init.tmp | sudo tee /etc/s6-linux-init/skel/rc.init > /dev/null

sed 's/# exec s6-rc -v2/exec s6-rc -v2/' /etc/s6-linux-init/skel/runlevel  | tee /tmp/runlevel.tmp > /dev/null
cat /tmp/runlevel.tmp | sudo tee /etc/s6-linux-init/skel/runlevel > /dev/null

sed 's/# exec s6-rc -v2/exec s6-rc -v2/' /etc/s6-linux-init/skel/rc.shutdown | tee /tmp/rc.shutdown.tmp > /dev/null
cat /tmp/rc.shutdown.tmp | sudo tee /etc/s6-linux-init/skel/rc.shutdown > /dev/null



STAMP=$(date +%s)
sudo s6-linux-init-maker -c /etc/s6-linux-init/init-${STAMP} -G "/usr/bin/agetty tty2 38400 linux" -1 /etc/s6-linux-init/init-${STAMP}
sudo s6-ln -s /etc/s6-linux-init/init-${STAMP} /etc/s6-linux-init/current


sudo s6-rc-compile /etc/s6-rc/compiled-init1 /etc/s6-rc/source/*
sudo s6-ln -snf /etc/s6-rc/compiled-init1 /etc/s6-rc/compiled

sudo groupadd -g 981 s6log
sudo useradd -m -d /var/lib/s6log -s /bin/nologin -g s6log -u 981 s6log

sudo cp /etc/s6-linux-init/current/bin/* /sbin/

# yolo:
sudo /sbin/reboot-void
5 Likes

Just a quick warning, don’t use this yet, working on the script, which is certainly broken. In retrospect, I should’ve tested before editing.

Working. Please test this in a VM in whatever configuration you want. Feel free to comment bellow if you find any bugs.

1 Like

Because I’ll KMS if I have to do this manually so many times, scripted way to add the sshd service. Needs to be added before the s6-rc-compile /etc/s6-rc/compiled-init1.

sudo s6-ln -snf /etc/s6-rc/s6sv/20-ok-multi-user/sshd /etc/s6-rc/source/20-ok-multi-user/
sudo s6-ln -snf /etc/s6-rc/s6sv/20-ok-multi-user/openssh-server /etc/s6-rc/source/20-ok-multi-user/
sudo s6-ln -snf /etc/s6-rc/s6sv/20-ok-multi-user/openssh-server-log /etc/s6-rc/source/20-ok-multi-user/
sudo touch /etc/s6-rc/source/01-ok-all/ok-multi-user/contents.d/sshd

Having to git reset because dev is testing in the master branch is total insanity. IMO, builds should be reproducible and working when you’re cloning a master branch and you should have different branches (like -test or something) where you dump all the commits and then you test if things work. When you’re satisfied, you make a big commit and push it to master.

I’ll have to work on packaging these with stable versions. The services part, which I’m the one maintaining the git repo, seems to be working and I’m expecting that this should work with most distros. Unfortunately, I can’t account for different versions of skarnet programs distros would be using, meaning there’s a slight chance that some older program version might not run well with the services source I’m maintaining.

E.g. there was a tool called s6-test, which got deprecated and a new one created in execline (el-test), because the test utility is so useful outside of s6 that it was worth it to change it. That meant that all the services I cloned from some places that were using s6-test were no longer working.

Similar situations can happen if a distro uses the latest stable, while others are maintaining older stable (tested) packages. Don’t get me started about how LTS releases (and packages) are a scam. If my services are tested with the latest s6 packages (and I’m using el-test) and someone uses an ancient version of packages (in this case an old execline that doesn’t contain el-test), then services compilation of s6-rc db will succeed, but when you reboot or try to restart services, you’re in for a surprise when things don’t match up.

The reason this is opinionated and makes heavy use of s6 tools is because different distros have different packages (then there’s BSDs too, which I’m considering to support in the probably very distant future), so you can’t use stuff like GNU core-utils test on an alpine linux server (where you’ve only got busybox).

Unfortunately there were a few places I had to compromise (like remounting / as rw using the normal mount command, instead of using s6-mount, because otherwise people needed to edit the service for their own entry, instead of just using the fstab), but I’ll try to minimize these whenever I can. And where I can’t, I’ll just have to maintain the source services for different distros. Thankfully, these should basically never change.

1 Like

I found another aspect of the way I want to deploy s6 and wanting to configure my own way of doing things.

So, there’s now 3 folders that s6-rc utilizes, during db compilation and during normal operations:

  • /etc/s6-rc/source
  • /etc/s6-rc/config
  • /etc/s6-rc/scripts

Being a lone sysadmin of one’s own system, one would just use these folders as-is as they are and not have to worry about something overwriting them (except maybe ransomware, but that’s outside the scope of this wiki).

But that’s hardly ever the case, unless you’re running LFS or something. Distro repo maintainers modifications will go head-to-head with the changes you make as an admin. So to get around this, I made a folder “s6sv” that will have all service definitions (sources) and will get overwritten by updates from a repo.

So far, all you have to do is run a symlink from s6sv to the sources folder. If services are updated, or if you install a new program and you want to add the service, you have the option of “enabling it” (by doing the symlink) and then “activate it” by recompiling the DB. To auto-start it, you also have to do add the service to one of the major bundles that get started on boot, otherwise, you can start it only on-demand.

Does it sound complicated? Well, it gets worse, because the folders “config” and “scripts” could be modified by a user, but then overwritten by an update from the repo of a service. This would ideally never happen, but programs evolve and get new features and parameters, which some distros might want to make use of, or the program itself changes behavior that now requires you use a certain explicit parameter.

This puts a user in a hard spot, because he can’t save his file from being overwritten (unless one uses something like apt and complicate the design of the package manager to ask the user if one wants to keep the locally modified file, or use the repo maintainer’s version).

So, as much as I hate having to devolve from the reference design, I’ll have to make a “repo-only / don’t touch” folder for each of these three paths (where all the changes from the repo goes) and have a user / local-system folder that a distro or repo program maintainer will never-ever touch (except for the first system install, or maybe even that would be a manual user configuration).

That allows a user to ensure his service definitions, additional opts and modified scripts will never get overwritten and if a user wants to use the repo version, one can just do a symlink to the repo’s service definitions. And the user will have the option to mix and match, use a repo service, but manually override the OPTS, or just do his custom thing (i.e. instead of symlinking, make a hard-copy of the service files, configs and scripts and modify as per their needs).

Which means now I have to make some kind of helper-script to make the transition easier than symlinking 3 paths at once and have the user choose whether one wants to symlink some or all services, configs and scripts (and maybe add flags for automation / non-interactive mode), or make a hard-copy of them.

I was thinking of using /usr/share/examples, but I’m afraid /usr could potentially be a different file system (which could happen on some distros that don’t combine everything in /usr/bin and still maintain the /bin, /sbin, /usr/bin and /usr/sbin separate hierarchy). That means /usr/share/examples might not be available when the system boots, for the symlinks for /etc/s6-rc/scripts and /etc/s6-rc/config for services to work, which would result in a fail to boot in certain scenarios, which I find unacceptable.

So I’ll probably end up having a folder under /etc/s6-rc which handles all the “do-not-touch” stuff. The size isn’t going to be that large, only services of software you install should be added and removed from there.

But I have to admit, s6-rc puts a lot of these controls back into the user’s hand, which is quite a difference compared to something like ubuntu that hand-holds the user, like start enabling services on startup immediately upon installation and even worse, sometimes restarting services because an update has ran.

The unfortunate part is that, if a service definition changes, you have to either tell the user to recompile the rc db, or recompile it without the user’s consent, but with the contents of the source folder (which ideally should be configured as the user desired anyway) and run an s6-rc-update. That’s getting dangerously close to what ubuntu is doing, if automated, so I’d much rather prefer that distros get out of the way and let the users do their own system’s maintenance work (there’s no reason why a user wouldn’t want to update some packages now, but restart services at a later time).

I’m not 100% sure if s6-rc-update will stop a service that’s already running if its run file changed or not. I know it will stop services that aren’t defined anymore, like if you’ve removed a package. And speaking of removing a package, if you had it defined in s6-rc, you again want to recompile the db, with the service file or symlink removed (otherwise s6-svscan will infinitely try to start a service that doesn’t exist anymore and always fail). The user has to be very conscientious about his system.

Another point I have to hammer down is that there’s a pretty big limitation to the way I’m doing things.

A user can always just add the whole server definitions folder to the source and compile everything under the sun. And s6-rc will handle about 1000 services just fine (maybe even more). And if they aren’t all enabled by default (by touching the service in the proper bundle’s contents.d), then they won’t be attempted to be started. However, an s6-svscan will be generated for each, unfortunately.

But my idea is to have system states accurate to what’s currently present on the system and things that don’t exist have no place in the s6-rc db (e.g. say you have a dropbear service defined in your db, but dropbear isn’t installed anymore - there’s no reason for it to be there, meaning your system state db is literally “out of date”).

The way I’m doing it entails doing some manual maintenance (it could be automated to some degree in the package manager, but idk that much about this topic), but really ensures a system is clean, lean and mean, if one takes care of it.

So, one must add and remove stuff from the sources and compile the s6-rc db whenever one does a change on the system. While it’s cool to automate things and make one’s life easier, it shouldn’t be the system’s job to keep track of what the user’s doing (unless it’s nixos, where it’s supposed to replicate any changes - I guess that’s one place where s6-rc would shine in an automatic mode, on a nix-os rebuild x).