Sign in
← Linux: booting
12 min · medium · Lesson 5 of 7

systemd as init: targets and boot order

systemd as init: targets, unit files, and parallel boot.

Why this matters

The server rebooted, and your service didn't come up, even though you ran systemctl enable. Or a service only fails at boot, while starting it by hand works on the first try. Both stories come down to how systemd decides what to start and in what order. This lesson covers targets and the dependency graph the boot process follows.

Who is PID 1

By this point the boot chain has already run: firmware -> bootloader -> kernel -> initramfs. The kernel unpacked initramfs into rootfs (ramfs, or tmpfs if the kernel has CONFIG_TMPFS enabled), found the real root, mounted it, and handed control to the first userspace process.

That process is systemd, and it gets PID 1. systemd manages the rest of the daemons. A daemon here means a background service with no terminal: sshd, cron, the network stack. systemd starts them, watches them, and restarts them on failure if the service's settings say to.

ps -p 1 -o pid,comm
    PID COMMAND
      1 systemd

Unit files instead of scripts

systemd's init instructions live in declarative unit files instead of shell scripts. Declarative means the file describes a desired state (what to start, after what, under which conditions), not a top-to-bottom sequence of commands.

One unit describes one entity: a service, a mount point, a socket, a timer or a target. You look at the loaded units like this:

systemctl list-units --type=service
systemctl status ssh

The first command shows active services, the second shows one service's state (on Ubuntu the SSH service is named ssh, on Fedora and RHEL it's sshd). How a unit file itself is structured and what each field means is covered in detail in the linux-admin/systemd-units lesson. Here, two lines from it matter:

Targets: system states

A target is a named group of units that systemd brings into an active state. A target is reached once all its dependencies are running. The target itself doesn't do anything; it just groups other units.

Targets you'll run into constantly:

TargetWhat it means
sysinit.targetearly initialization: local filesystems, swap, udevd, tmpfiles
basic.targetsockets, timers, paths: the foundation for services
multi-user.targetserver mode, services with no GUI
graphical.targeteverything from multi-user plus a graphical login

default.target sets which target the boot starts from. It's a link to one of the targets, and you check it like this:

systemctl get-default
multi-user.target

You can change the default target with systemctl set-default <target>. It rewrites the /etc/systemd/system/default.target link.

Loaded targets:

systemctl list-units --type=target
  UNIT                   LOAD   ACTIVE SUB    DESCRIPTION
  basic.target           loaded active active Basic System
  ...
  multi-user.target      loaded active active Multi-User System
  ...
  sysinit.target         loaded active active System Initialization
  ...

The key consequence for services. At boot, systemd only starts what the default target pulls in, directly or through other targets. If a service is attached to a target the boot doesn't reach, it stays inactive after a reboot, even though systemctl is-enabled will honestly say enabled.

Order comes from the graph, not a queue

graphical.target pulls in multi-user.target, which pulls in basic.target, which pulls in sysinit.target. A graphical session won't come up before the basic initialization does.

systemd has two independent kinds of relations between units:

A requirement by itself doesn't set an order. If two units only have Wants= or Requires= between them, systemd starts them at the same time. The chain of targets above is ordered because each target has both kinds of relations: for example, multi-user.target has both Requires=basic.target and After=basic.target. You can check this like so:

systemctl show -p Requires -p After multi-user.target

Anything not connected through After=/Before= starts in parallel as soon as its own conditions are met. The order between two independent units isn't defined: one might come up first today, and the other tomorrow.

systemctl list-dependencies graphical.target
graphical.target
● ├─...
● └─multi-user.target
●   ├─...
●   ├─basic.target
●   │ ├─...
●   │ ├─sysinit.target
...

The indentation in the output shows who pulls in whom, not start time. Units at the same level start in parallel if there's no After= between them.

How long the boot took

systemd-analyze
Startup finished in 2.1s (kernel) + 4.8s (userspace) = 6.9s
graphical.target reached after 4.7s in userspace.

This is an example from an ordinary machine. The numbers on yours will differ.

Two more commands show which units held up the boot:

Targets and runlevels

A target solves the same problem as a runlevel in SysV init: which mode the system runs in. But it's built differently. A runlevel was a number from 0 to 6 with a fixed set of scripts. A target is a node in the dependency graph, there can be as many targets as you like, and you describe your own target with a unit file.

For compatibility, systemd has alias targets: runlevel3.target points to multi-user.target, runlevel5.target to graphical.target.

You can switch a running system to another target with isolate. It starts everything the new target needs and stops everything it doesn't:

systemctl isolate multi-user.target

isolate only works on targets that have AllowIsolate=yes. multi-user.target, graphical.target, and rescue.target have it, basic.target and sysinit.target don't. On a server with an open SSH session, isolate rescue.target will stop sshd too, so only try it in a lab or on a machine with console access.

Common mistakes

What to remember

The first userspace process is systemd with PID 1. Init instructions live in declarative unit files.

A target is a named group of units. multi-user.target has no GUI, graphical.target has one. systemctl get-default shows the default target.

Boot follows the graph. Requirements (Wants=, Requires=) say what to start, ordering (After=, Before=) says what to wait for. Anything not ordered starts in parallel.

The next lesson covers udev: how the system discovers devices and populates /dev.

What you'll need for the tasks

Done reading?

Mark the lesson as read to move your track progress forward.

Go through it with an AI agent

The agent explains anything unclear in plain words, gives you 2 labs for this lesson and drills you on interview questions. $49 one-time, lifetime access.

Get access for $49

Tell the agent: use MCP izi, give me lesson linux-boot-05-systemd-targets