A Brief History of Init Systems

Updated

October 30, 2026

A Brief History of Init Systems

When the Linux kernel completes initialization, it calls run_init_process("/sbin/init"). As Process ID 1, this executable is the ancestor of every userspace process on the machine.

Over the past four decades, operating system initialization has evolved across four distinct eras.

       Era 1: SysVinit (1980s-2000s)
       - Sequential, shell-script driven, numbered runlevels (0-6)
       - Fragile PID tracking, slow serial execution
                      |
                      v
       Era 2: Event-Driven Prototypes (mid 2000s)
       - Upstart (Ubuntu), Launchd (macOS)
       - Asynchronous event bus, reactive daemon launching
                      |
                      v
       Era 3: The systemd Revolution (2010-Present)
       - Socket activation, cgroup process containment, declarative units
       - Aggressive parallelization, integrated logging (journald)
                      |
                      v
       Era 4: The Minimalist Counter-Movement
       - OpenRC (dependency tree without binary bloat)
       - s6 & runit (composable supervision trees, UNIX philosophy)

Era 1: Classic System V Init (SysVinit)

Originally developed for AT&T UNIX System V, sysvinit organized system states into numeric runlevels: - 0: Halt - 1 / S: Single-user rescue mode - 3: Multi-user text mode with networking - 5: Multi-user graphical mode with X11 - 6: Reboot

Services were managed by numbered shell script symlinks in /etc/rc<runlevel>.d/ pointing to /etc/init.d/:

/etc/rc3.d/S10network -> /etc/init.d/network
/etc/rc3.d/S20ssh     -> /etc/init.d/ssh
/etc/rc3.d/S30cron    -> /etc/init.d/cron

SysVinit executed these scripts sequentially in alphabetical order. Because each shell script had to fork multiple subshells (grep, sed, awk, cat), boot times were slow. Tracking processes required brittle /var/run/*.pid files; if a daemon double-forked and crashed, the init system had no way of knowing whether the process was alive.

The Push for Concurrency and Containment

Modern machines feature dozens of CPU cores and hot-pluggable USB and NVMe devices. An init system cannot assume hardware is static at power-on. Modern systems require: 1. Parallel Activation: Booting services concurrently across available CPU cores. 2. Reliable Process Containment: Using Linux cgroups to track every child process spawned by a service, preventing orphaned daemons. 3. Socket and Bus Activation: Delaying daemon startup until a connection arrives on a UNIX socket or D-Bus endpoint.