Re: rcd(8) - new service manager daemon

"Enji Cooper (yaneurabeya)" <[email protected]> Tue, 16 Jun 2026 10:09:12 -0700
Newsgroups gmane.os.freebsd.devel.hackers
Message-ID <[email protected]>
> On Jun 14, 2026, at 12:26 PM, Baptiste Daroussin <[email protected]> wrote:
> 
> Hello everyone
> 
> I have been working for the past years on rcd(8), a new service manager
> daemon for FreeBSD, some of you might recall a presentation in french 15
> years ago or some discussion in BSDcam also that old!
> I would like to start a discussion about its integration into the tree.
> 
> What is rcd?
> 
> rcd(8) is a service manager daemon called by init(8) (in place of
> /etc/rc).  It reads service definitions from UCL unit files
> (/etc/rcd.d/*.ucl), builds a dependency DAG, and starts services in
> parallel.  After boot completes, it forks to background and stays
> running as a supervision daemon (automatically restarting failed
> services and accepting control commands via a UNIX socket).
> 
> Key features:
> 
>  - Parallel boot via dependency DAG (no more serial rc.d execution)
>  - Process tracking via pdfork(2) descriptors (no PID file races)
>  - Subreaper via procctl(2) (no orphaned process escape)
>  - Socket activation (pre-bound sockets passed via fd inheritance)
>  - Resource control per service via rctl(2)
>  - Service isolation via native jail(2) integration
>  - OOM protection via procctl(2) PROC_SPROTECT
>  - UCL-based unit files (JSON Schema validated)
>  - Embedded Lua interpreter for inline service hooks
>  - Template units for per-instance services (e.g., dhclient@em0)
>  - Safe in-place binary upgrade (SIGUSR1: save state, re-exec)
>  - Per-service access control on the control socket
>  - Suspend/resume support
> 
> User interface: rcctl(8)
> 
> Service management is done via rcctl(8):
> 
>    rcctl start sshd
>    rcctl enable sshd
>    rcctl restart dhclient@em0 netif@em1
>    rcctl status
>    rcctl show nginx
> 
> 100% backward compatibility
> 
> This is a hard requirement: rcd must work on existing FreeBSD systems
> without modifying any rc.d scripts or configuration files.  Here is how
> this is achieved:
> 
>  1) rcd scans /etc/rc.d/ and /usr/local/etc/rc.d/ for existing
>     rc.d scripts, parses their PROVIDE/REQUIRE/BEFORE/KEYWORD headers
>     (same format as rcorder(8)), reads rc.conf(5) to determine the
>     enabled state, and wraps each script as a virtual "legacy" unit
>     in the dependency graph.
> 
>  2) Legacy scripts are auto-classified during loading:
>     - Scripts with pidfile= or command= -> "legacy-forking" units,
>       tracked by rcd-exec(8) sub-reaper without pidfiles.
>     - Scripts with only comments/blank lines -> barrier units.
>     - Scripts without rcvar= are always enabled.
> 
>  3) All rcctl(8) commands are passed through to the script directly:
>         rcctl reload sshd -> /bin/sh /etc/rc.d/sshd reload
> 
>  4) Template instances preserve the traditional calling convention:
>         rcctl restart netif@em0 netif@em1
>         -> /bin/sh /etc/rc.d/netif restart em0 em1
> 
>  5) rcd reads /etc/defaults/rc.conf, /etc/rc.conf and
>     /etc/rc.conf.local to determine legacy service enablement.
> 
> The result: you can install rcd on a running system, reboot, and
> everything works exactly as before -- except boot is faster because
> services start in parallel where the dependency graph allows.
> 
> Current status and what I need
> 
> I would like to commit this to main soon so that people can start
> testing it on their machines.  The initial commit will include:
> 
>  - sbin/rcd/   -- the daemon, rcd-exec helper, unit tests
>  - sbin/rcctl/ -- the control utility
>  - Man pages: rcd.8, rcctl.8, rcd.conf.5, rcd.d.5, rcd-lua.3,
>    rcd-exec.8
> 
> It will NOT change init(8) or /etc/rc yet.  rcd will be built and
> installed but not activated by default.  To test, you will be able to
> run it manually or configure via kenv(8) to replace /etc/rc.
> 
> Migration path
> 
> The plan for a smooth transition:
> 
> - 1 (now): Commit rcd(8) and rcctl(8) to the tree.  Build by
>  default, but not activated.  Early adopters can test.
> 
> - 2 (later): Commit init(8) changes to call rcd when present (falling
>  back to /etc/rc otherwise).  Provide a /etc/rc wrapper that invokes
>  rcd for systems that want to switch.
> 
> - In the meantime: convert base system rc.d scripts to native
>  UCL unit files adding the norcd keywords to the rc.d so that they
>  are not read anymore by rcd(8).
> 
> - Ultimately: rc.d -> unit files, at the pace each maintainer sees fit.
>  There is no deadline; rc.d scripts are supported as long as needed.
> 
> The code lives there:
> https://reviews.freebsd.org/D56835

Some scenarios to consider:
- What happens if rcd dies? Who respawns it?
- How are UCL/schema changes tracked and how are upgrades/downgrades handled? It would be unfortunate if someone installed a version of world (for instance) that did not interoperate with rcd.

A general request:
- Please avoid the monolithic mess that is systemd and keep the dependency list (directly linked libraries) small. systemd is fast, all-singing-all-dancing, but of the Linux systems I manage, I can’t tell you the last time I didn’t have to do a reboot after an update because a library was upgraded or there was an issue found in the systemd ecosystem. I get frequently nagged by unattended-updates about needing to manually poke services. The rcd architecture avoiding DBus helps this to some degree, but I want to caution against tightly coupling everything like systemd does (it makes the ecosystem into a maintenance nightmare).

Thank you,
-Enji
signature.asc (application/pgp-signature, 833 B)
-----BEGIN PGP SIGNATURE-----

iQIzBAEBCAAdFiEEkHfexGRJ3gYRdA2gGpE5DjPsNJgFAmoxgzgACgkQGpE5DjPs
NJjB8A//WR3LH1PgusLK/lD+4Lh3wX/Ovya9zs/wtGmadXCCJnZ3OWxlMHcLbslc
8aNro/HqhilDDqdFfBhFcXUC/mUDfedAlV53gJKRIHKxcMXq2RZvvIdq4ZWR89tS
JyBaOi+E9QKyZcSZbyhMeKI7EOyWUXMYONLqhcAMN6KgFPQFqAaS1e5EUZmEumPk
qE7LbssC+dURCQ0/YCE24/04KCBHP5lmY907kS+8RICNX7ZTUSUQONB37RECLfKI
pr6myaE0nRriMy0WmNW20CfcpgXCjmDI1nxtidKh0zuArZaYldvLpGZqwixqid8j
Rg7WuwejnBUJ0NMAmMV7+gDabVdk7Hc8iKL5AzTFEfogrxxRgNzizfFLHj7bwMP3
GwUqlgkrwOOEM7od8GTVS9yi+mF2ZC/zJT8v9p0ZjdG7QHRhLrAPlkCBI3hvR1CJ
WQ1+nQpQVAYocNUaPHZAj3+eIzOCINKpFaB3BxvRpTftYhfR3niurbHbze6CO4vy
0fRSsFxDvqsiRoeqKd71kqvUfXsBHuznSsoXv6yzFQVmAv/CoUByNszCcgu7SldF
MqNZuEQqvUaM5wjMS9VXH5hpPeCQGDeefHnk0BWk/Ah+88auaTZ0DKJk4auDviT7
5ctLvTSUrq266rOreb+KNQJlMhhfd6P1ToH/7wvWZmqJQWWGLX0=
=kPg6
-----END PGP SIGNATURE-----