Skip to content
Back to blog

systemd Builder: How Linux Moved from Init Scripts to Unit Files

6 min read
systemd Builder: How Linux Moved from Init Scripts to Unit Files

systemd is often presented as a contest of taste: fast, modern booting on one side and Unix philosophy or an oversized project on the other. That framing obscures the practical reason distributions changed service managers. They needed a system that could do more than call a startup script. It had to understand relationships between services, determine when they were ready, track every process belonging to them, and manage them consistently after boot. systemd addressed those needs through a common unit model and became the default service manager in many distributions, although alternative init systems never disappeared.

What came before systemd

On a Unix-like system, the kernel starts process ID 1 before the rest of user space. That process brings up the system and adopts orphaned descendants. In the classic System V model, the desired machine state was expressed as a runlevel, and entering it invoked a collection of scripts from rc directories. Each script knew how to start, stop, or restart one daemon. The arrangement was transparent to administrators and served Unix systems well for decades.

Its limitation was not merely that it used shell. The common contract exposed too little of a service's lifecycle. A script could place a process in the background and exit before that process was actually ready to accept work. Daemons commonly wrote PID files, double-forked, and dropped privileges themselves, with each project implementing the details somewhat differently. The startup manager saw the script's result but did not always have a reliable view of the complete service or every descendant it had created.

It is also inaccurate to claim that every SysV-style boot was strictly sequential. Debian, for example, used insserv to calculate ordering from LSB headers and enabled parallel execution of independent init scripts by default in May 2010. This layer improved boot time and honored declared dependencies, but the relevant information remained split among an imperative script, its header, a PID file, and the daemon's own behavior. The deeper problem was the uneven contract between those parts, not a total absence of dependency handling or concurrency.

Other service managers were already exploring alternatives. Solaris SMF represented service dependencies and could restart failed services, macOS launchd used event and socket activation, and Ubuntu developed the event-driven Upstart. systemd did not emerge in an empty field. It combined several established ideas and coupled them to Linux kernel facilities in a new model.

A project that began by rethinking PID 1

On April 30, 2010, Lennart Poettering published “Rethinking PID 1” and introduced an experimental init project. At that stage, he had written most of the code base, while the design had been developed in close cooperation with Kay Sievers and input from several other engineers. The objective was not simply to exchange a directory of shell scripts for another configuration format. The service manager was meant to participate in the full lifecycle of the processes it started.

The central object became a unit rather than a startup command. A unit can describe a service, socket, mount, device, timer, target, or another resource managed by systemd. When a target or service is requested, the manager constructs a transaction from related units, checks it for conflicts, and performs the jobs in a valid order. Independent branches of that graph can proceed concurrently.

Two kinds of relationship must be kept separate. Wants= and Requires= determine which units are pulled into a transaction, whereas Before= and After= only order jobs that are already scheduled. The official systemd.unit documentation calls requirement and ordering dependencies orthogonal: After=network.target alone does not start the network target. This separation makes the graph more precise, but it still requires the author to understand the directives. Declarative syntax can conceal an incorrect relationship just as neatly as it can describe a correct one.

Socket activation supplied another important piece. The manager can create a listening socket in advance, begin launching dependent services, and pass the file descriptor to the responsible daemon when it is ready. A client does not have to wait for a separate “service fully started” milestone because requests can queue briefly in the socket. This expands the opportunity for concurrent booting, but its value is broader than a faster stopwatch result: it also provides an explicit activation boundary and lets some services start on demand.

Comparison of traditional System V init execution with declarative systemd unit dependencies and socket activation

What cgroups contribute

A traditional daemon could double-fork, change its PID, and create additional processes, leaving a PID file to identify only one selected process. systemd places the processes of each service in a separate Linux control group and reads kernel-maintained membership to see the whole group no matter how often a program forks. Stopping a unit can therefore address all of its processes, while resource accounting and limits can be applied to the same stable boundary.

cgroups do not solve the Unix zombie-process problem. A zombie is a child that has exited while its parent has not yet called wait() to collect its status. If the parent exits, the child is adopted by init or the nearest subreaper, which must then call wait(); this is a normal PID 1 responsibility documented by wait(2). cgroups address a different problem by preserving the membership of living processes in a service and giving the manager a durable boundary around them.

Where init ends and the systemd suite begins

The name systemd refers both to the system and service manager running as PID 1 and to a wider software project. The project describes itself as a suite of basic building blocks for Linux. It includes journald for logs, logind for sessions, networkd for networking, resolved for name resolution, timesyncd for clock synchronization, and tools for temporary files, system accounts, and other operating-system tasks. These are not one enormous process running inside PID 1, and distributions can build, enable, or replace many of them separately.

The growing suite became one reason the argument continued. Shared conventions can simplify distribution integration, diagnostics, and administration, particularly when components exchange state through common interfaces. The cost is greater overall complexity, deeper use of Linux-specific facilities, and the possibility that functions which are separate in theory become difficult to substitute in practice. Saying that “systemd violates Unix philosophy” signals a position, but it does not explain this concrete tradeoff between integration and interchangeability.

journald, timers, and resolved also deserve narrower claims than they often receive. The systemd journal stores structured records and can filter them by unit, boot, and other fields, while forwarding to traditional syslog remains possible. Timer units can replace some cron jobs, but cron still exists. systemd-resolved ships in the project, but each distribution decides whether to use it as the machine's DNS component.

Debian's decision and the creation of Devuan

On February 11, 2014, the Debian Technical Committee decided that systemd would be the default init system for Linux architectures in Debian Jessie. The discussion covered more than boot speed: implementation maturity, desktop integration, package maintenance, and the cost of supporting several init systems all mattered. Selecting a default did not immediately prohibit every alternative, but it strengthened concerns that package dependencies would make those alternatives increasingly expensive to maintain.

The Devuan project launched at the end of November 2014, retaining compatibility with Debian while making operation without systemd one of its defining goals. Devuan now uses sysvinit by default and offers alternatives including OpenRC and runit. Its continued existence shows why the story is not an uncomplicated technical victory. Most large distributions chose systemd's integrated model, while another part of the community kept developing systems with different boundaries and priorities.

What systemd Builder does

systemd Builder generates a basic .service unit for a conventional foreground process using Type=simple. Its form accepts the service name and description, ExecStart, working directory, user, group, restart policy and delay, and a line of environment assignments. The generated file includes After=network.target, the standard [Unit], [Service], and [Install] sections, and installation under multi-user.target.

The tool does not construct an arbitrary dependency graph, create timer units, or switch the service type to forking, notify, or oneshot. Its purpose is narrower: it provides a readable starting point for an application that stays in the foreground and can be managed as a simple service. A second tab supplies commands for saving the file, running daemon-reload, enabling and starting it with enable --now, and checking its status. The unit or command file can be copied or downloaded.

To begin, enter an absolute command such as /usr/bin/node /var/www/app/index.js, the working directory, and an unprivileged user, then choose Restart=on-failure. After saving the file, verify directory permissions and environment values, run sudo systemctl daemon-reload, start the service, and inspect it with systemctl status. A production unit will often add access restrictions, startup conditions, and dependencies tailored to the application.

Frequently asked questions

Who created systemd?

Lennart Poettering introduced the first version of systemd in 2010. In his original description, he wrote that most of the code base was his work, while the design details were developed closely with Kay Sievers and contributions from other engineers.

Why is systemd controversial?

The debate concerns the scope of the wider project as much as the init process. The systemd repository contains a service manager, journal, and components for sessions, networking, DNS, time, and other system functions. Supporters value shared interfaces and close integration; critics worry about complexity, Linux-specific dependencies, and less choice between interchangeable components.

What was SysV init?

The name commonly describes boot schemes derived from Unix System V, in which init changes runlevels and invokes scripts from rc directories. Implementations could account for dependencies and run independent scripts in parallel, but service management still relied on imperative shell scripts and external conventions such as PID files.

What is a systemd unit file?

A unit file is a declarative, INI-like file describing an object managed by systemd. For a service, [Unit] contains its description and relationships, [Service] defines the command and process-management rules, and [Install] states which target should pull the unit in when it is enabled.

Do all Linux distributions use systemd?

systemd is the default in many widely used distributions, including Debian, Ubuntu, Fedora, and RHEL, but it is not a required part of Linux. Alpine uses OpenRC, Void Linux uses runit, and Devuan keeps sysvinit as its default while offering other init systems.

Author

Fullstack Engineer

In commercial development since 2011 — over 15 years. Author of two SaaS products, TeleGo.io and LingvoHabit.com. The articles here come out of real client and product work.

About the authorWrite on Telegram

Related Articles