Appendix H

SysV, Upstart,
and systemd

Every Linux system runs one process before anything else touches the disk. What that process is, and how it talks to every other service, changed twice. This appendix covers the switch — and the day-to-day commands that came with it.

Runlevels → targets
Unit files & logs
enable · disable · mask
Presets & defaults

Boot, Explained

One process starts everything else.

The kernel starts exactly one program before handing off control: PID 1, the init system. Every other process on the machine — every service, every login shell, every daemon — descends from it. Lesson 01's glossary called this "systemd" and moved on. Here's what came before it, and why it changed.

SysV init is the original Unix model: a fixed sequence of numbered runlevels (0–6), each backed by shell scripts in /etc/init.d/. Boot order came from filename prefixes — S20ssh runs before S50apache2 — with dependency hints bolted on afterward as comment headers that a separate tool (insserv) parsed to compute ordering. It works, and it's easy to read one script at a time. It is also strictly sequential: nothing starts until the thing before it finishes, whether or not the two are actually related.

A footnote, not a chapter: Upstart. Canonical shipped Upstart as Ubuntu's default init from 2006 through 2014, trading SysV's static ordering for an event-driven model. It never spread much beyond Ubuntu and its derivatives, and Ubuntu itself dropped it for systemd in 15.04. You'll see the name in old forum posts. You won't touch it on a current system.

systemd replaced both. Instead of numbered scripts, services declare their dependencies directly, and systemd builds a dependency graph and starts everything it safely can in parallel — which is most of why boot got faster. One command, systemctl, front-ends the whole system instead of a family of ad hoc scripts and helper tools. It became the default on Debian 8 (2015), Ubuntu 15.04, and RHEL 7, and is what you're running today on Mint, Debian, and nearly everything else this course covers.

Boot Targets

Runlevels became targets.

systemd doesn't use runlevels internally, but it keeps compatibility names for them — targets — so old habits and old documentation still map onto something real.

SysV runlevelsystemd targetMeaning
0poweroff.targetHalt and power off
1 / S / singlerescue.targetSingle-user, maintenance mode
2, 3, 4multi-user.targetFull multi-user, no GUI (systemd treats these three as equivalent)
5graphical.targetMulti-user plus display manager / GUI
6reboot.targetReboot

There's also emergency.target, one step below rescue.target: it mounts almost nothing and gives you a root shell on a barely-alive system, for when rescue.target itself won't come up cleanly.

# What do we boot into by default?
systemctl get-default

# Change the default (takes effect next boot)
sudo systemctl set-default multi-user.target

# Switch right now, without rebooting
sudo systemctl isolate rescue.target

Careful with isolate. Switching to rescue.target or emergency.target stops most running services on the spot — including, often, your network connection. Fine on a console in front of the machine; a bad idea over SSH unless you're prepared to lose the session.

Under Each Service

Scripts vs. unit files.

A SysV init script is an imperative program: a case "$1" in start|stop|restart|status) block that you write the logic for, one script per service. A systemd unit file is declarative instead — you describe what the service needs and how it should start, and systemd figures out the sequencing.

[Unit]
Description=OpenSSH server daemon
After=network.target

[Service]
ExecStart=/usr/sbin/sshd -D
Restart=on-failure

[Install]
WantedBy=multi-user.target
# illustrative — shaped like a real service unit, not a byte-exact copy

After= is ordering only — it says nothing starts sequencing until network.target is up, but doesn't require it to succeed. Requires= is a hard dependency: if it fails, this unit fails too. Wants=, far more common in practice, is the soft version — best-effort, no failure propagation. WantedBy= in [Install] is what systemctl enable actually acts on: it's the target that will pull this unit in at boot.

Every unit resolves through three directories, checked in this order — the first one that has a file wins:

DirectoryPrecedenceWhat lives here
/etc/systemd/system/HighestAdmin config — enable symlinks, masks, overrides
/run/systemd/system/MiddleRuntime only, volatile — cleared at reboot
/usr/lib/systemd/system/ (/lib/ is the same directory)LowestVendor-shipped unit files, installed by packages

To tweak one directive on a vendor unit without copying the whole file, systemctl edit NAME creates a drop-in at /etc/systemd/system/NAME.d/override.conf that layers on top of the original — the vendor file stays untouched and upgrade-safe. After hand-editing or adding any unit file directly, systemd won't notice until you tell it to:

sudo systemctl daemon-reload

Where It All Gets Written

syslog vs. the journal.

SysV-era logging means rsyslog writing plain text to /var/log/syslog, /var/log/auth.log, and friends — readable with nothing but grep and tail, and still present on most systems today. systemd adds journald, a structured binary journal that automatically captures stdout/stderr from every service it manages, tagged with metadata (unit, PID, boot ID, priority) you can query directly.

# Everything logged by one unit
journalctl -u ssh

# Follow it live
journalctl -u ssh -f

# Since the current boot (-b -1 for the previous one)
journalctl -b

# Time-windowed
journalctl --since "1 hour ago"

# Errors and worse, across everything
journalctl -p err

The journal may not survive a reboot. On many distros it's volatile by default, kept only in /run/log/journal and wiped at shutdown. If you want history across reboots — which matters the moment you're investigating anything — sudo mkdir -p /var/log/journal && sudo systemctl restart systemd-journald makes it persistent.

Day to Day

Controlling what runs.

One command handles everything: starting and stopping a service right now, and separately, whether it starts automatically at boot.

systemctl start NAME # run it now
systemctl stop NAME # stop it now
systemctl restart NAME # stop, then start
systemctl reload NAME # re-read config, keep it running (if supported)
systemctl status NAME # state + recent log lines

sudo systemctl enable NAME # start automatically at boot
sudo systemctl disable NAME # stop starting automatically
systemctl is-enabled NAME
systemctl is-active NAME

sudo systemctl mask NAME # block it from starting at all, even manually
sudo systemctl unmask NAME

On a system still carrying SysV habits, service NAME start and sudo update-rc.d NAME defaults do roughly the same two jobs — worth recognizing on older boxes, even though everything in this course uses systemd directly.

Enable, disable, and mask are three different symlink mechanisms, not three strengths of the same setting.

Both enable and mask take a --runtime flag that redirects the symlink to /run/systemd/system/ instead — same effect, but gone at the next reboot. That's the layer most people forget to check.

# Where is this actually resolving from?
systemctl status NAME # "Loaded:" line shows the winning path
systemctl cat NAME

# Check each tier by hand
ls -la /etc/systemd/system/NAME
ls -la /run/systemd/system/NAME
ls -la /usr/lib/systemd/system/NAME

Security lens. disable stops a service from autostarting, but a dependency or a package postinst can still start it later. mask is the stronger control — nothing can start it, period. Reach for mask when you want a service genuinely gone. But "masked" only tells you the state, not the layer: it can be an admin's persistent choice in /etc/, a temporary --runtime mask in /run/ that vanishes on reboot, or a vendor package that ships the unit pre-masked with no admin action involved at all. The same rule holds as everywhere else in security work: an enabled or masked service you can't explain — and can't locate — is the first thing worth investigating.

Before You Ever Touched It

Why some services start already configured.

A fresh install already has opinions about what should be enabled. That's not an accident — it's presets: policy files that declare a default enable/disable state per unit, applied automatically when a package installs, before you've ever run systemctl enable yourself.

Vendor policy lives in /usr/lib/systemd/system-preset/*.preset; a local override of that policy goes in /etc/systemd/system-preset/*.preset, checked first. Files are evaluated in order, one line per unit, first match wins:

# /usr/lib/systemd/system-preset/90-default.preset
enable ssh.service
disable bluetooth.service

Package installers apply this policy with systemctl preset NAME — the actual mechanism behind "this new service starts enabled by default" on a fresh system, instead of every package's install script hardcoding an enable call. It's also a real candidate for the alsa-utils case from earlier in this course: a preset, or a maintainer script running a --runtime mask during a package transition, is a plausible explanation for a masked unit with nothing to find in /etc/.

Security lens. systemctl preset-all resets every unit on the system back to policy defaults in one shot — including anything you've manually enabled, disabled, or masked as part of hardening. Never run it without diffing enabled units before and after. On a security-sensitive image, the preset files are worth reading once: they're the layer that decided what was running before you ever logged in.