← Back

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.

.servicemost common

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.

.socketactivation

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.

.timerscheduling

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.

.mountfilesystem

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.

.targetgrouping

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:

/etc/systemd/system/myapp.service
[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 nginx

Start a unit immediately

systemctl stop nginx

Stop a running unit

systemctl restart nginx

Stop then start again

systemctl reload nginx

Reload config without killing the process. Only works if the service supports it

Inspect

systemctl status nginx

Current state, recent log lines and process info all in one place

systemctl is-active nginx

Prints active or inactive. Exit code 0 if running, useful in scripts

systemctl is-enabled nginx

Tells you whether the unit will start at boot

systemctl list-units --type=service

All loaded service units and their current state

Boot behaviour

systemctl enable nginx

Create the symlinks that make the unit start at boot

systemctl disable nginx

Remove those symlinks

systemctl mask nginx

Prevent the unit from starting at all, even manually

systemctl daemon-reload

Reload 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.target

System is shutting down

rescue.target

Single-user mode with a root shell. Useful for recovery work

multi-user.target

Full multi-user system without a graphical interface. Where most servers end up

graphical.target

Multi-user with a graphical session layered on top

network-online.target

Network 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.

/etc/systemd/system/backup.timer
[Unit]Description=Run backup every night at 02:00 [Timer]OnCalendar=*-*-* 02:00:00Persistent=true [Install]WantedBy=timers.target

Persistent=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 nginx

Current state, last few log lines and the exit code on failure. This is always the first place to look.

journalctl -u nginx -n 50

The 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.