OpenRC and other init systems¶
On most of the distributions this knowledge base covers, you manage services with systemctl. On Gentoo you'll more often use rc-service and rc-update, because Gentoo defaults to OpenRC. The commands differ, but the larger difference is in how each system is designed. That determines how services are defined, how failures are detected, where logs go, and how much of the system the init software controls.
This page explains those design choices. Command-by-command equivalents belong in reference material; here the goal is to understand why the systems behave differently.
What "init" actually means¶
When the kernel finishes booting, it starts one userspace program as process ID 1 (PID 1). Everything else on the system descends from it. Discussions of "init systems" usually mix together four separate jobs:
| Job | What it involves |
|---|---|
| PID 1 | Being the first process; adopting and reaping orphaned processes so they don't linger as zombies; never exiting (if PID 1 dies, the kernel panics); starting shutdown or reboot. |
| Service manager | Deciding which services to start, in what order, based on dependencies, and stopping them cleanly. |
| Process supervisor | Watching running daemons and restarting them when they die. |
| Surrounding plumbing | Logging, mounting filesystems, device events, user sessions, timers, network setup. |
Init systems differ mainly in which of these jobs they take on, and whether they combine them in one project. That one idea explains most of the comparison below:
- SysV init is a minimal PID 1 plus a set of shell-script conventions.
- OpenRC is primarily a service manager. It can run on top of another PID 1 and supervises only if you ask it to.
- runit, s6 and dinit are built around supervision, with dinit and s6-rc adding dependency management.
- systemd combines all four, plus much of the surrounding plumbing.
flowchart TB
subgraph SysV["SysV init"]
direction TB
s1["PID 1: /sbin/init<br/>(reads /etc/inittab)"] --> s2["rc scripts in /etc/init.d<br/>run in a fixed order"]
end
subgraph OpenRC["OpenRC (Gentoo, Alpine)"]
direction TB
o1["PID 1: sysvinit, BusyBox init<br/>or openrc-init"] --> o2["OpenRC service manager<br/>(dependency resolution)"]
o2 --> o3["optional supervisor:<br/>supervise-daemon or s6"]
end
subgraph systemd["systemd"]
direction TB
d1["PID 1 = service manager = supervisor<br/>(units, cgroups, jobs)"] --> d2["journald, logind, udevd,<br/>networkd, resolved, timers…"]
end
A short history¶
SysV init (from AT&T UNIX System V, standard on Linux through the 2000s) defined numbered runlevels (0 = halt, 1 = single user, 3 = multi-user, 5 = graphical, 6 = reboot). Each runlevel had a directory of symlinks named like S20ssh and K80ssh, pointing to shell scripts in /etc/init.d/. The numbers set the start (S) and kill (K) order. This was simple and transparent, but the ordering was a global sequence of numbers, not real dependencies. Services started one at a time, and once a daemon had backgrounded itself, nothing watched it.
Several projects tried to fix these weaknesses in different ways:
- Gentoo's rc scripts added named dependencies to init scripts. Starting around 2007, Roy Marples reworked them into a standalone, portable project: OpenRC. It became Gentoo's stable default around 2011, and is also the default on Alpine Linux.
- Upstart (Ubuntu, 2006) made boot event-driven. It was used by Ubuntu and RHEL 6 before being replaced by systemd.
- runit (Gerrit Pape) and Daniel J. Bernstein's earlier daemontools focused on reliable supervision: each service runs in the foreground under a small supervisor that restarts it if it exits.
- systemd (Lennart Poettering and Kay Sievers, first released in 2010) took ideas from Apple's launchd and socket activation, and integrated service management with Linux-specific kernel features, especially control groups (cgroups). Fedora adopted it in 2011, RHEL 7 in 2014, and Debian 8 and Ubuntu 15.04 in 2015. It is now the default on Debian, Ubuntu, RHEL and its derivatives, Fedora and Arch Linux.
Later projects such as s6 (Laurent Bercot) and dinit (Davin McCall; the default on Chimera Linux) continue the supervision-first approach, adding dependency management and readiness notification.
How OpenRC works¶
OpenRC is a service manager, not necessarily PID 1¶
Historically, OpenRC doesn't replace PID 1. On Gentoo it traditionally runs under sysvinit: /etc/inittab tells sysvinit to call openrc with a runlevel name. On Alpine, BusyBox init is PID 1 and calls OpenRC in the same way. Newer OpenRC releases also ship openrc-init, a small PID 1 of its own, but the design still keeps "being PID 1" and "managing services" as separate concerns.
This separation is deliberate. It keeps the most critical process (PID 1) small, and it lets OpenRC run on different PID 1 programs and even on other operating systems; OpenRC has supported FreeBSD and NetBSD.
Named runlevels¶
OpenRC keeps the runlevel idea but uses names instead of numbers. Each runlevel is a directory of symlinks in /etc/runlevels/:
sysinit: very early system setup (/proc,/sys,/dev)boot: things every boot needs (hostname, filesystems checked and mounted, clock, kernel modules)default: normal operation (e.g.sshd,nginx,chronyd)shutdown: the shutdown sequence
You can create your own runlevels (for example a maintenance runlevel with networking but without application services) and switch between them. rc-update add sshd default simply creates the symlink /etc/runlevels/default/sshd.
Unlike SysV, membership of a runlevel says what should run, not in what order. Order comes from dependencies.
Service scripts with declared dependencies¶
An OpenRC service is a script in /etc/init.d/ run by openrc-run. It's still a shell script, but most of the logic comes from shared OpenRC functions, so a typical modern script is short and largely declarative:
#!/sbin/openrc-run
# /etc/init.d/exampled
description="Example daemon"
command="/usr/sbin/exampled"
command_args="${EXAMPLED_OPTS}"
command_background=true
pidfile="/run/${RC_SVCNAME}.pid"
depend() {
need net
use logger dns
after firewall
}
The depend() function separates two ideas that SysV merged into one number:
- Requirement strength:
need(hard: start it, and fail without it),want(start it if possible),use(use it if it's already in the runlevel). - Ordering:
afterandbefore, which don't pull services in.
There is also provide, for virtual services: several services can provide logger or net, and a dependent service doesn't need to know which one is installed. Settings that belong to the administrator rather than the package go in /etc/conf.d/<service>, and system-wide behaviour goes in /etc/rc.conf.
Scripts can still define their own start(), stop() and extra commands (for example reload or checkconfig), because they are shell scripts. That flexibility is one of OpenRC's main attractions, and it's also where inconsistency can creep in.
Tracking and supervising daemons¶
By default, OpenRC starts a daemon with start-stop-daemon and then tracks it by PID file. If the daemon later crashes, OpenRC by default doesn't notice: rc-status may still show it as started (or as crashed, if the check finds the process has gone), but nothing restarts it. This is the classic SysV-era behaviour.
OpenRC offers two ways to get real supervision, chosen per service:
supervise-daemon, OpenRC's own supervisor, enabled withsupervisor=supervise-daemonin the service script or itsconf.dfile. It keeps the daemon in the foreground as its child and restarts it according torespawn_*settings.- Integration with s6, for those who want a dedicated supervision suite.
On Linux, OpenRC can also put each service into its own cgroup (rc_cgroup_mode in /etc/rc.conf). This means stopping a service can reliably kill every process it created, even ones that escaped the PID file. The same problem is what pushed systemd to build on cgroups from the start.
Parallel startup¶
OpenRC can start independent services in parallel (rc_parallel="YES"), but it's off by default. The OpenRC documentation warns that console output gets mixed up and some edge cases are less tested. Serial startup with dependency ordering is the conservative default, in keeping with the project's focus on predictability.
Logging¶
OpenRC doesn't include a logging system. Daemons log as they always have, to syslog (provided by a separate package such as sysklogd, metalog, syslog-ng or rsyslog) or to their own files. supervise-daemon can redirect a service's output to files. There is no equivalent of the systemd journal unless you install a separate logger.
How systemd differs¶
systemd starts from a different premise: the init system should know everything about the services it runs, and should use Linux kernel features to do so.
- Declarative units instead of scripts. A service is an INI-style unit file (
[Unit],[Service],[Install]), not a program. Behaviour that OpenRC scripts implement in shell (users, environment, restart policy, sandboxing, resource limits) is expressed as directives likeUser=,Restart=on-failure,ProtectSystem=strictorMemoryMax=. Unit files are easier to analyse and harder to get subtly wrong, but you can only do what a directive exists for. - Many unit types. Besides
.servicethere are.socket,.timer,.mount,.path,.targetand others, so the init system takes over jobs that used to belong toinetd,cronandfstabhandling. - Dependencies and ordering, as in OpenRC, but more detailed.
Requires=/Wants=say what is pulled in;After=/Before=say the order. The two are separate in systemd too. The main conceptual overlap with OpenRC is this split; systemd adds a transactional "job" engine that computes the whole set of changes before applying them. - Targets instead of runlevels.
multi-user.targetandgraphical.targetplay the part of runlevels, but they're just units that group other units, and several can be active at once. - Every service in its own cgroup, always. systemd always knows exactly which processes belong to which service. PID files are unnecessary and "the daemon forked and escaped" doesn't happen.
- Supervision built in. Restart policies, watchdogs and readiness notification (
Type=notify) are part of PID 1. - Parallel by default, helped by socket activation: systemd can create a service's listening socket before the service starts, so clients can connect early and dependent services don't need to wait.
- An integrated suite.
journald(structured, indexed logs),logind(sessions),udevd(devices), and optionallynetworkd,resolved,timesyncdand more, all developed together.
Supervision-first alternatives¶
| System | Core idea | Where you'll meet it |
|---|---|---|
| runit | Three-stage design: one-time boot tasks, then runsvdir supervises a directory of services, then shutdown. A service is a directory with a run script that runs the daemon in the foreground; sv controls it; svlogd handles logs. Very small, but no dependency management beyond what the run scripts do. |
Void Linux (default), Artix Linux (option) |
| s6 (+ s6-rc, s6-linux-init) | A set of small, composable programs: a supervision tree (s6-svscan, s6-supervise), a dependency-based service manager (s6-rc) and an init (s6-linux-init). Strong on readiness notification and correctness. |
Artix Linux (option); as a supervisor under OpenRC |
| dinit | One service manager plus supervisor, with dependencies and declarative service descriptions, intended as a middle ground between runit's minimalism and systemd's scope. Can run as PID 1 or as a user service manager. | Chimera Linux (default), Artix Linux (option) |
| BusyBox init | A tiny SysV-style PID 1 in a single binary, usually paired with OpenRC or simple scripts. | Alpine Linux, embedded systems |
Comparing the approaches¶
| SysV init | OpenRC | systemd | runit / s6 / dinit | |
|---|---|---|---|---|
| Scope | PID 1 + script conventions | Service manager (PID 1 optional) | PID 1, service manager, supervisor and a wide suite | Supervisor first; s6-rc and dinit add dependencies |
| Service definition | Shell scripts | Shell scripts using shared functions; mostly declarative variables | Declarative unit files | Small run scripts (runit, s6) or declarative files (dinit) |
| Ordering | Numbered sequence | Named dependencies (need, use, want, after, before, provide) |
Named dependencies and ordering; transactional jobs | None (runit); dependency graph (s6-rc, dinit) |
| Process tracking | PID files | PID files, supervisor, optional cgroups | cgroups, always | Supervisor, always |
| Restart on crash | No (except respawn in inittab) |
Opt-in (supervise-daemon, s6) |
Built in (Restart=) |
Built in, the core purpose |
| Parallel start | No | Opt-in | Yes, plus socket activation | Yes |
| Logging | syslog | syslog or per-service files | journald (optionally forwarding to syslog) | Per-service loggers (svlogd, s6-log) or syslog |
| Portability | Unix-wide | Linux and BSDs | Linux only | Linux and others (runit, s6) |
The trade-offs¶
Each design trades one property for another.
Transparency vs integration. OpenRC's scripts are plain shell, so you can read exactly what a service does, run it with sh -x, and change its behaviour with ordinary shell. systemd's units are less flexible, but because systemd understands them rather than just executing them, it can sandbox, account for, restart and analyse services in ways a script-based system can only approximate.
Small, separable parts vs one coordinated whole. OpenRC, runit and s6 follow a Unix tradition: small tools with narrow responsibilities that you combine yourself, a small PID 1, and the freedom to replace any part. systemd's components are developed together and assume each other, which gives consistency (one logging system, one way to express dependencies, one set of tools on every systemd distribution). The cost is that adopting systemd means adopting much of the stack, and it ties you to Linux and its kernel features.
Conservative defaults vs maximum use of the kernel. OpenRC defaults to serial startup and PID-file tracking, with supervision, parallelism and cgroups as options. systemd always uses cgroups, parallelism and supervision. On servers, boot speed matters less than predictability and recovery, which narrows the practical gap. The remaining differences are mostly about restart behaviour and observability.
Ecosystem gravity. Most Linux software now ships systemd unit files upstream, and some desktop components assume systemd's logind (alternatives such as elogind exist for non-systemd systems). On OpenRC systems, the distribution often provides the service scripts. Gentoo and Alpine maintain them carefully, but the work exists. For the four distribution families covered here, that means systemd knowledge transfers across Debian/Ubuntu, the RHEL family and Arch, while OpenRC knowledge mainly applies to Gentoo (and Alpine).
Why Gentoo defaults to OpenRC¶
OpenRC grew out of Gentoo's own rc system, so the defaults reflect Gentoo's values: administrator choice, small composable parts, and being able to see and change how the system works. Gentoo also supports systemd fully through systemd profiles. A Gentoo system with a systemd profile manages services like any other systemd distribution; the differences from Debian or Arch are in packaging, not in init.
In practice, the parts of administration that change between OpenRC and systemd are:
- Enabling and controlling services: runlevel membership (
rc-update) andrc-serviceinstead ofsystemctl enableandsystemctl start. - Writing services: an
openrc-runscript withdepend()instead of a unit file. - Crash recovery: restarts only if you configure a supervisor.
- Logs: a syslog daemon and files in
/var/loginstead ofjournalctl. - Timers and scheduled jobs: cron (e.g.
cronie) instead of.timerunits. - Boot problems: OpenRC's runlevels (
sysinit,boot,default) instead of targets; see How to boot into rescue mode.
Most other administration (networking tools, storage, packages, firewalls) is unaffected by the choice of init system.
Related¶
- Explanation: The Linux boot process
- How-to: How to run a script as a service
- How-to: How to boot into rescue mode
- Reference: systemd unit directives
Further reading¶
- OpenRC project and documentation, including the service-script guide and supervise-daemon documentation
- Gentoo Wiki: OpenRC and Gentoo Wiki: systemd
- systemd documentation and the
systemd.unit(5),systemd.service(5)man pages - Lennart Poettering, "Rethinking PID 1" (2010): the design argument for systemd
- runit, s6 and skarnet.org, dinit
init(8),inittab(5)andopenrc-run(8)man pages