18.1 systemd as PID 1: Init Replacement and Service Manager
Alright, let’s get our hands dirty. Forget the old SysVinit scripts with their cryptic start/stop numbers and the philosophical debate about whether the ‘S’ stood for ‘start’ or ‘sorry, this is needlessly complicated’. systemd isn’t just a new init system; it’s a take-over artist that has bought the building, evicted the old management, and installed a hyper-efficient, slightly obsessive, and occasionally infuriating new superintendent. And it starts by becoming PID 1. That’s a big deal. In the Unix world, PID 1 is the ancestral parent of every other process on the system. When the kernel finishes booting, it hands control over to this first process, which is then responsible for bringing up the rest of the userland. This isn’t a job you give to just any daemon. systemd takes this role and, frankly, runs with it further than anyone thought possible.
Why systemd Had to Replace Init
The old SysVinit system worked by shell scripts. So many shell scripts. To start a service, you’d have a script in /etc/init.d/ that would, hopefully, daemonize a process correctly. This was a messy, blocking, and slow process. Scripts ran sequentially, so if your mysql script took 10 seconds to start, your apache script (which needs MySQL) just had to sit there and wait, twiddling its thumbs. There was no concept of dependency tracking beyond the order of script filenames (hence the S01this, S02that nonsense in /etc/rcX.d/). It was like building a house by telling one contractor to show up, and only when he’s completely finished do you call the next one. systemd looked at this and said, “This is absurd.” It uses a parallelized, socket-based approach to bring up services as fast as possible, as soon as their dependencies are met, not in some arbitrary sequence.
The Core Concept: Units and Unit Files
systemd manages system resources with units. A unit is a configuration file that describes a thing to be managed. That thing could be a service (*.service), a mount point (*.mount), a timer (*.timer), a socket (*.socket), or even a slice of system resources (*.slice). This is systemd’s “do everything” nature that some love and others… tolerate. The unit files typically live in /lib/systemd/system/ (for packages from your distro) and /etc/systemd/system/ (for your custom stuff and overrides). Let’s look at a simple service unit file for a hypothetical daemon, myapp:
# /etc/systemd/system/myapp.service
[Unit]
Description=My Brilliant Application
Documentation=https://example.com/docs
After=network.target syslog.target
[Service]
Type=simple
ExecStart=/usr/local/bin/myapp --serve
ExecReload=/bin/kill -HUP $MAINPID
Restart=on-failure
User=myappuser
Group=myappgroup
[Install]
WantedBy=multi-user.target
Let’s break this down. The [Unit] section is metadata and dependencies. After= means this unit should start after the listed units are active. Crucially, it doesn’t mean it requires them; for that, you’d need Requires=. The [Service] section is where the magic happens. Type=simple tells systemd the process it forks with ExecStart is the main daemon itself. Don’t use this if your script daemonizes itself (forks into the background); in that hilarious case, you’d use Type=forking and systemd would have to just guess what the child PID is. It’s a relic we’re stuck with. Restart=on-failure is a lifesaver, automatically resuscitating your app if it crashes. Finally, [Install] tells systemd where to hook this unit if you enable it.
Socket Activation: The Killer Feature
This is the genius part. Instead of starting a service and having it immediately open a socket and listen, systemd can create the socket itself first. When a connection comes in on that socket, systemd will then instantly start the service that’s supposed to handle it and hand the socket off. This means services can be started on-demand, zero-cost until they’re needed. It also means you can often start services in any order. If Service B needs a socket from Service A, it doesn’t matter if A is running yet; systemd holds the socket open, and the connection attempt will trigger A’s start. This is how we get parallelized, faster bootups.
Pitfalls and Sharp Edges
systemd is powerful, but it has its quirks. The journal (journalctl) is brilliant for centralized logging, but its default pager is less, and if you forget the flags, you’ll be there all day. Protip: journalctl -u myapp.service -f --since "5 minutes ago" is your best friend. Another common gotcha is forgetting to tell systemd you changed a unit file. After any edit, you must run sudo systemctl daemon-reload. It doesn’t watch the filesystem for changes because, of course, that would be too convenient. Also, while systemctl status is fantastic, its idea of “success” can be broad. A service can be “active (exited)” which means it ran and exited cleanly (good for oneshot tasks) or “active (running)” (a persistent daemon). You have to actually read the output.
Ultimately, systemd as PID 1 is a paradigm shift. It’s a huge, complex system that brings modern service management to Linux. It can be bureaucratic, but the bureaucracy is there for a reason: reliability, speed, and control. You don’t have to like it, but you do have to understand it if you want to run a modern Linux system.