Skip to content

Units and Targets


A unit is systemd’s basic building block — one configuration file, describing one thing systemd manages. Not every unit is a service, though, and understanding the different types, along with how systemd groups them into an overall system state called a target, is what makes the rest of this section make sense.

Unit Types

Units are distinguished by their file extension, and each type manages a different kind of resource:

ExtensionManages
.serviceA program or daemon — the type you’ll work with most
.mountA filesystem mount point
.socketA network or IPC socket, often used to start a service on demand when something connects to it
.timerA scheduled, recurring trigger — systemd’s alternative to cron
.targetA named grouping of other units, representing a system state
.deviceA piece of hardware the kernel has detected
.pathA trigger based on a filesystem path changing

You’ll spend most of your practical time with .service and .target units in this section — the others are worth recognizing on sight, but won’t come up hands-on here.

Where Unit Files Actually Live

Unit files aren’t scattered arbitrarily — they live in a small number of fixed locations, and where a given unit file sits determines how easily it can be overridden.

ls /etc/systemd/system
ls /lib/systemd/system

/lib/systemd/system (sometimes /usr/lib/systemd/system, depending on the distribution) holds unit files installed by packages — this is where a package’s apt install puts the service definition it ships with. /etc/systemd/system holds units an administrator has created or customized directly, and it takes priority over the package-provided version when both exist. This mirrors a pattern already familiar from elsewhere: package-managed content stays in its own managed location, while local, administrator-owned customization goes in /etc, exactly the separation /etc has represented throughout this course.

Exploring What’s Actually Loaded

Two systemctl subcommands — covered fully in the next chapter — are worth previewing here just for exploration:

systemctl list-units --type=service

Shows every service unit currently loaded into systemd, running or not.

systemctl list-unit-files --type=service

Shows every service unit systemd knows about on disk, including ones that aren’t currently loaded or active, along with whether each is enabled to start automatically.

Targets: Grouping Units Into A System State

A target doesn’t run anything itself — it’s a named collection of other units, representing a coherent overall system state. Booting the system to a normal, usable, multi-user command-line environment means reaching multi-user.target, which in turn depends on a whole collection of individual service units all being active — networking, logging, and so on.

This directly replaces the older SysV concept of runlevels — numbered system states (0 through 6) that predate systemd entirely. You’ll still see runlevel numbers referenced in older documentation and habits, so the mapping is worth knowing:

Old RunlevelModern systemd TargetMeaning
0poweroff.targetShut the system down
1rescue.targetSingle-user, minimal recovery mode
3multi-user.targetFull multi-user, no graphical interface
5graphical.targetMulti-user, with a graphical interface
6reboot.targetRestart the system

There’s also emergency.target, with no direct old-style runlevel equivalent — an even more minimal state than rescue.target, typically only reached automatically when something has gone wrong early enough in boot that even rescue.target’s dependencies can’t be satisfied.

Checking And Changing The Default Target

The default target is whichever one the system boots into normally — check it directly:

systemctl get-default

On a typical server with no graphical interface, this returns multi-user.target. Changing it is equally direct:

sudo systemctl set-default multi-user.target

This is a genuinely common real task — converting a desktop-oriented installation into a headless server, for instance, means setting the default target to multi-user.target instead of graphical.target, so the system stops trying to start a graphical environment it may not even have hardware or drivers to support.

Switching Targets Without Rebooting: isolate

sudo systemctl isolate rescue.target

isolate switches the running system to a different target immediately, stopping any units that the new target doesn’t need and starting any it does — effectively transitioning system state live, without a reboot.

Warning

isolate can stop a large number of running services at once, including ones you didn’t intend to touch, if the target you’re switching to doesn’t include them. Switching to rescue.target or emergency.target on a remote system you’re connected to over SSH is a particularly easy way to lock yourself out — the networking and SSH service themselves may not survive the transition, since those minimal targets aren’t guaranteed to include them as dependencies. Confirm exactly what a target includes before isolating into it on any system you don’t have physical access to.

Dependency Directives: A Preview

Unit files express their relationships to each other through directives like Wants=, Requires=, After=, and Before=Wants= and Requires= describe which other units should also be active, while After= and Before= control ordering without implying a dependency at all. Full unit file syntax, including these directives in context, is the subject of a later chapter — for now, just recognize that this is how a target like multi-user.target actually knows which individual services it depends on, rather than that grouping being some kind of hardcoded special case.

What’s Next

With units and targets understood conceptually, the next chapter makes it practical: systemctl, the single command responsible for starting, stopping, checking, and enabling nearly everything covered in this chapter.

Last updated on