What Is systemd? A Beginner's Guide
Short answer: systemd is the init system and service manager that runs as PID 1 on most Linux distributions — Ubuntu, Debian, Fedora, RHEL, Arch. It boots the machine, starts and supervises services, collects their logs, and handles timers, mounts, and sockets. You drive it with systemctl and read its logs with journalctl.
If you've typed something like systemctl status and wondered what is systemd actually doing underneath, this is the practical version — the parts you touch every day, the commands that matter, and the two or three gotchas that trip up almost everyone.
So what is systemd, exactly?
When the Linux kernel finishes bringing up your hardware, it hands control to a single user-space process with process ID 1. That process is responsible for starting everything else and adopting any orphaned processes. On nearly every mainstream distro today, that PID 1 process is systemd.
But systemd does more than start services and then get out of the way. It keeps running for the life of the machine, watching each service, restarting the ones that crash (if you tell it to), grouping their processes with cgroups, and piping their output into a central log. It also manages scheduled jobs, filesystem mounts, and network sockets. That breadth is exactly why some people love it and others grumble — more on that below.
The name follows the old Unix habit of ending daemon names in "d". You'll see it written lowercase, systemd, not "SystemD" or "System D".
Why did Linux replace init with systemd?
For about two decades, most Linux systems used SysVinit, inherited from AT&T's System V Unix. It read /etc/inittab, switched the system into a numbered runlevel, and ran shell scripts from directories like /etc/rc3.d/ one after another to bring services up.
It worked, but it had real problems. Scripts ran sequentially, so boot was slow. Dependencies were faked through filename ordering (S10networking before S20ssh). If a service died after startup, nothing brought it back. And writing a correct init script was genuinely fiddly — PID files, lock handling, backgrounding with & and nohup.
Lennart Poettering and Kay Sievers, then at Red Hat, started systemd in 2010 to fix all of that. Poettering's April 2010 post "Rethinking PID 1" laid out the pitch. Fedora 15 shipped it as the default in May 2011; Debian 8 and Ubuntu 15.04 both switched in April 2015. Today it's the default nearly everywhere.
| Aspect | SysVinit | systemd |
|---|---|---|
| Startup | Sequential shell scripts | Parallel, dependency-based |
| Service config | Scripts in /etc/init.d/ | Declarative unit files |
| Control command | service / chkconfig | systemctl |
| Dependencies | Filename ordering (S10, S20) | After=, Requires=, Wants= |
| Crash recovery | Manual restart | Automatic (with Restart=) |
| Logging | Text files + syslog | journald / journalctl |
| Boot state | Runlevels (0–6) | Targets |
The core idea: everything is a unit
systemd manages resources called units. A unit is described by a plain-text file, and its extension tells systemd what kind of thing it is. Once you internalise this, the rest of systemd stops feeling like a pile of unrelated commands.
| Unit type | Extension | What it manages |
|---|---|---|
| Service | .service | A daemon or process (the one you'll use most) |
| Socket | .socket | A socket for on-demand (socket) activation |
| Target | .target | A group of units — systemd's take on runlevels |
| Timer | .timer | Scheduled activation, a cron alternative |
| Mount | .mount | A filesystem mount point |
| Path | .path | Watches a file or directory and activates on change |
| Swap | .swap | Swap space |
| Slice | .slice | A cgroup for grouping and limiting resources |
A minimal service unit looks like this:
[Unit]
Description=My Application
After=network.target
[Service]
Type=simple
ExecStart=/usr/local/bin/myapp
Restart=on-failure
[Install]
WantedBy=multi-user.target
Three sections do most of the work. [Unit] holds the description and dependencies, [Service] says how to run the thing, and [Install] tells systemd what to hook it into when you enable it.
systemctl commands you'll actually use
Nearly all day-to-day interaction goes through systemctl. The verbs that matter:
sudo systemctl start nginx # start it now
sudo systemctl stop nginx # stop it now
sudo systemctl restart nginx # stop then start
sudo systemctl reload nginx # reload config, no full restart
sudo systemctl status nginx # is it running? recent logs?
sudo systemctl enable nginx # start automatically at boot
sudo systemctl disable nginx # don't start at boot
You can drop the .service suffix — systemctl start nginx and systemctl start nginx.service are the same thing.
How do I check if a service is running?
systemctl status is the command I run more than any other. It packs a lot into one screen:
● nginx.service - A high performance web server
Loaded: loaded (/usr/lib/systemd/system/nginx.service; enabled; preset: enabled)
Active: active (running) since Sat 2026-07-04 09:12:31 UTC; 2h 3min ago
Docs: man:nginx(8)
Main PID: 1236 (nginx)
Tasks: 3 (limit: 4915)
Memory: 8.7M
CPU: 234ms
CGroup: /system.slice/nginx.service
├─1236 "nginx: master process /usr/sbin/nginx"
└─1237 "nginx: worker process"
Two lines carry most of the signal. Loaded tells you where the unit file came from and whether it's enabled (starts at boot). Active tells you the current state — active (running), inactive (dead), or the one you don't want:
Active: failed (Result: exit-code) since Sat 2026-07-04 09:20:11 UTC; 5s ago
When you see failed, the last few log lines usually appear right below the status output. For the full story, jump to journalctl (next section).
To list things instead of checking one service:
systemctl list-units --type=service --state=running # running services
systemctl --failed # anything that failed
systemctl list-unit-files --type=service # all installed, enabled or not
What's the difference between enable and start?
This is the single most common systemd mistake, so it's worth being blunt about it: enable and start are independent.
startruns the service right now, this boot. It does nothing about future boots.enablesets it to launch automatically at future boots. It does not start it now.
I've watched people run systemctl enable myservice, see no errors, and assume the service is up — then get paged because it isn't. Enabling only creates a symlink:
$ sudo systemctl enable nginx
Created symlink /etc/systemd/system/multi-user.target.wants/nginx.service → /usr/lib/systemd/system/nginx.service.
If you want both — running now and at every boot — do it in one shot:
sudo systemctl enable --now nginx
To enable or disable a service the other way, disable removes that symlink but leaves the service running until you also stop it. Want to make sure nobody — not even another service — can start something? Use mask, which points the unit at /dev/null. Undo it with unmask.
Where do systemd unit files live?
Unit files sit in three main locations, and systemd applies a strict precedence order. Higher on this list wins:
/etc/systemd/system/— your custom units and overrides. Highest priority. Edit here./run/systemd/system/— runtime units, generated on the fly. Gone after reboot./usr/lib/systemd/system/— units shipped by packages (some distros use/lib/systemd/system/). Don't edit these.
My strong opinion: never hand-edit a vendor file in /usr/lib. A package update will silently overwrite it, and you'll spend an afternoon wondering why your change vanished. Instead, use a drop-in override — a small file that changes only the directives you care about:
sudo systemctl edit nginx
That opens an editor and, on save, writes to /etc/systemd/system/nginx.service.d/override.conf. Your snippet layers on top of the vendor file, and package upgrades keep working. To see the fully resolved unit — vendor file plus every drop-in — run systemctl cat nginx.
The rule that saves you: any time you create or edit a unit file by hand, run
sudo systemctl daemon-reloadbefore you start or restart the service. Without it, systemd keeps using the old configuration from memory and your change appears to do nothing.
journalctl basics: reading the logs
systemd captures the stdout and stderr of every service into a central, indexed log managed by journald. You query it with journalctl. Don't bother memorising the whole flag list — four options cover almost everything:
journalctl -u nginx # logs for one unit
journalctl -u nginx -f # follow live, like tail -f
journalctl -b # only this boot (-b -1 = previous boot)
journalctl -p err # only errors and worse
The combination I reach for when a service won't start:
journalctl -xeu nginx.service
That's -u (this unit), -e (jump to the end), and -x (add catalog help text explaining common errors). Add -n 100 to cap it at the last 100 lines.
Filtering by priority uses numeric levels 0–7, and -p shows that level and everything more severe:
| Level | Name | Level | Name |
|---|---|---|---|
| 0 | emerg | 4 | warning |
| 1 | alert | 5 | notice |
| 2 | crit | 6 | info |
| 3 | err | 7 | debug |
Time filtering is more readable than you'd expect:
journalctl --since "1 hour ago"
journalctl --since "2026-07-04 08:00" --until "2026-07-04 10:00"
journalctl -u ssh --since "30 min ago" -f
One gotcha: on some distributions the journal isn't persistent by default, so journalctl -b -1 returns nothing after a reboot. Fix it by creating the directory and letting journald use it:
sudo mkdir -p /var/log/journal
sudo systemctl restart systemd-journald
Or set Storage=persistent in /etc/systemd/journald.conf.
Targets: systemd's version of runlevels
Where SysVinit had numbered runlevels, systemd has targets — named groups of units that represent a system state. The one most services attach to is multi-user.target (a full command-line system with networking), which maps roughly to the old runlevel 3. graphical.target adds a desktop. You rarely set these by hand, but it's handy to know:
systemctl get-default # which target you boot into
sudo systemctl set-default multi-user.target # boot to CLI, no GUI
sudo systemctl isolate rescue.target # switch to single-user now
Is systemd a good thing? The honest take
systemd is standard for a reason: parallel boot is faster, unit files are consistent across distros, and having one tool that manages services, logs, timers, and mounts genuinely cuts down on the "every distro does it differently" pain that used to make cross-distro work miserable.
The criticism is also fair. systemd absorbs a lot of functionality that used to live in separate tools, which some see as violating the Unix "do one thing well" philosophy and enlarging what PID 1 is responsible for. A handful of distributions deliberately avoid it — Devuan (sysvinit), Void (runit), Alpine and Gentoo (OpenRC). If you administer Linux for a living, though, you'll be using systemd on the vast majority of machines, so learning it well is time well spent.
FAQ
Is systemd only an init system?
No. Its core job is PID 1 — booting the system and supervising services — but it also ships journald for logging, timers as a cron alternative, socket and path activation, mount management, and more. That scope is deliberate, and it's the main thing critics dislike.
How do I list all running services?
Run systemctl list-units --type=service --state=running. For a full inventory including inactive and disabled units, use systemctl list-unit-files --type=service.
What's the difference between stop and disable?
stop halts the service in the current session; it will still start at the next boot if it's enabled. disable prevents it from starting at boot but leaves it running now until you also stop it. To do both at once: systemctl disable --now <service>.
Why do I need to run daemon-reload?
systemd caches unit files in memory. After you create or edit a unit file, sudo systemctl daemon-reload re-reads them from disk so your changes take effect. Skip it and systemd keeps using the stale configuration.
Does every Linux distribution use systemd?
Most major ones do — Ubuntu, Debian, Fedora, RHEL and its rebuilds, openSUSE, Arch. Notable holdouts include Devuan, Void, Alpine, and Gentoo (which defaults to OpenRC). Check your distribution's current default if it matters for your setup.
How do I find out why a service failed?
Start with systemctl status <service> for the state and last log lines, then journalctl -xeu <service> for the full log with explanatory text. The -x flag often points you straight at the cause.
Where to go next
The fastest way to get comfortable is on a throwaway VM: write a tiny .service unit that runs a shell script, enable --now it, break it on purpose, and dig the failure out with journalctl -xeu. Do that two or three times and systemd stops being mysterious. For the exact semantics of any directive, the official man pages are the source of truth — systemctl and journalctl are the two you'll open most.