2026
systemd: The Init System That Runs Everything
A practical guide to systemd. Units, targets, timers and the systemctl commands you will actually use when managing Linux services.
When you boot a Linux machine, the kernel loads, finds pid 1 and hands control over to it. That process becomes the parent of everything else on the system. For the last decade on most major distributions, that pid 1 has been systemd.
It replaced a collection of shell scripts paired with a simple init daemon that most systems inherited from Unix System V. The old approach worked but had real limitations. Services started one by one in sequence, dependency management was done by hand and logging was scattered across a dozen different files in /var/log.
systemd consolidated all of that into one coherent system. It manages services, handles mount points, responds to device events and provides a unified logging layer through systemd-journald. Understanding it properly makes everything from debugging a failed boot to writing your own service file much more straightforward.
Everything is a unit
The core concept in systemd is the unit. A unit is a configuration file that describes a resource systemd should manage. The type of resource is encoded in the file extension.
A file ending in .service describes a daemon. A file ending in .timer describes a schedule. A file ending in .mount describes a filesystem mount point. The configuration format is similar across all of them, which makes it easy to read an unfamiliar unit file once you know the pattern.
A background process or daemon. nginx, sshd and postgresql all run as service units. If you are managing a running process, this is the unit type you want.
Defines a socket that systemd listens on. The corresponding service only starts when something actually connects. Useful for reducing boot time on systems with many seldom-used services.
A cron replacement. Triggers another unit on a calendar schedule or after a time interval. Fully integrated with the journal so you get real log output instead of emails to root.
Manages a mount point as a proper unit with dependencies. Lets you express that a service should only start after a particular filesystem is available.
A synchronisation point that groups units together. Represents system states like multi-user mode or network availability. Think of it as a named milestone the boot process works toward.
Anatomy of a service file
A service unit file is divided into three sections. Here is a minimal example for a custom application:
[Unit]Description=My ApplicationAfter=network-online.targetWants=network-online.target [Service]Type=simpleUser=myappWorkingDirectory=/opt/myappExecStart=/opt/myapp/bin/serverRestart=on-failureRestartSec=5 [Install]WantedBy=multi-user.target[Unit]Metadata and dependency declarations. After= controls ordering. This unit will not start until network-online.target is reached. Wants= is a soft dependency that requests the target without hard-failing if it is unavailable.
[Service]The actual process configuration. ExecStart is the command systemd will run. Restart=on-failure brings the process back if it exits with a non-zero code. User= drops privileges so the service runs as a dedicated account rather than root.
[Install]Controls what happens when you run systemctl enable. WantedBy=multi-user.target means this service gets pulled in when the system reaches normal multi-user mode.
Managing units with systemctl
systemctl is your interface to everything systemd manages. These are the commands you will use on a daily basis.
State
systemctl start nginxStart a unit immediately
systemctl stop nginxStop a running unit
systemctl restart nginxStop then start again
systemctl reload nginxReload config without killing the process. Only works if the service supports it
Inspect
systemctl status nginxCurrent state, recent log lines and process info all in one place
systemctl is-active nginxPrints active or inactive. Exit code 0 if running, useful in scripts
systemctl is-enabled nginxTells you whether the unit will start at boot
systemctl list-units --type=serviceAll loaded service units and their current state
Boot behaviour
systemctl enable nginxCreate the symlinks that make the unit start at boot
systemctl disable nginxRemove those symlinks
systemctl mask nginxPrevent the unit from starting at all, even manually
systemctl daemon-reloadReload unit files from disk. Required after editing any unit file
Easy to forget
After editing a unit file you must run systemctl daemon-reload before systemd will pick up the changes. It does not watch unit files for modifications. Skip this step and you will be running the old version without knowing it.
Targets
Targets are systemd's answer to the runlevels in old SysV init. They represent named system states. When the machine boots it works toward a final target, pulling in every unit that target depends on along the way.
The default target on most servers is multi-user.target. Check what yours is set to with systemctl get-default and change it with systemctl set-default.
poweroff.targetSystem is shutting down
rescue.targetSingle-user mode with a root shell. Useful for recovery work
multi-user.targetFull multi-user system without a graphical interface. Where most servers end up
graphical.targetMulti-user with a graphical session layered on top
network-online.targetNetwork is confirmed reachable. Services that depend on connectivity declare this as a requirement
Timers
systemd timers are a proper replacement for cron. Each timer unit is paired with a service unit that does the actual work. The timer file just controls when the service fires.
The advantages over cron are real. Output goes to the journal like any other service so you get searchable logs. You get proper dependency handling. You can inspect timer state at any time with systemctl list-timers.
[Unit]Description=Run backup every night at 02:00 [Timer]OnCalendar=*-*-* 02:00:00Persistent=true [Install]WantedBy=timers.targetPersistent=true is the detail worth paying attention to. If the machine was off at 02:00, the timer fires as soon as the system comes back up. With cron, a missed job is just gone.
Where unit files live
systemd looks for unit files in several directories. The order matters because a file in a higher-priority location overrides the same filename in a lower-priority one.
/lib/systemd/system/Shipped by packages. Do not edit these directly or your changes will be overwritten on the next package update.
/etc/systemd/system/Your overrides and custom units. Files here take precedence over the package defaults.
/run/systemd/system/Runtime units generated at boot. Not persistent across reboots.
When you want to override a value from a package-installed unit without touching the original file, use systemctl edit nginx. It creates a drop-in file under /etc/systemd/system/nginx.service.d/override.conf containing only the fields you want to change. The rest are inherited from the original.
When things go wrong
Two commands cover most debugging situations:
systemctl status nginxCurrent state, last few log lines and the exit code on failure. This is always the first place to look.
journalctl -u nginx -n 50The last 50 log lines from that unit. Useful when status output is too brief to show the actual error.
If status output does not tell you enough, run journalctl -xe to see the full context around the most recent boot or failure event.