skip to content

In systemd, what is a target unit, and why is it not simply a renamed SysV runlevel?

level: middleimportance: should knowfreq 60%

answer

  1. a unit that runs no process
  2. grouping plus synchronisation point
  3. one scalar versus many active at once
  4. dependency graph, not numbered sequence
  5. isolate is the telinit analogue

basics

~20 s

A systemd target is a named unit that groups other units and acts as a synchronisation point. Unlike a SysV runlevel, which was a single number with exactly one value active, many targets are active at once and reaching them is dependency-driven, not a numbered sequence.

solid answer

~40 s

A target is a systemd unit type that runs no process of its own; it exists to group other units and to serve as a named milestone you can depend on or wait for. A SysV runlevel was a single scalar state — the system was in runlevel 3 *or* 5, never both, and `telinit` moved between them. Targets are not exclusive: on a running desktop, `sysinit.target`, `basic.target`, `multi-user.target` and `graphical.target` are all active simultaneously, because `graphical.target` pulls in `multi-user.target`. Boot order is derived from declared dependencies and runs in parallel, rather than from two-digit numbers on symlinks. `default.target` replaces the `initdefault` line in `/etc/inittab`, and `systemctl set-default` changes it. For compatibility, `runlevel3.target` and `runlevel5.target` are aliases for `multi-user.target` and `graphical.target`, and `systemctl isolate` is the nearest equivalent of `telinit`.

go deeper

for a junior

Be able to translate the vocabulary confidently: multi-user.target is the text-mode server state, graphical.target adds the desktop, and default.target is what the machine aims for at boot.

for a middle

Explain the model shift, not just the mapping: a target is a unit that runs nothing, several are active simultaneously, and activation order comes from a computed dependency graph rather than numbered symlinks.

for a senior

Demonstrate judgment about when to touch targets at all — knowing that isolate tears down unrelated units, that changing the default target is a persistent host change, and that ordering problems are fixed by declaring relationships rather than renumbering.

for a principal

Own the consequence for fleet design: boot state should be reproducible from declared configuration or images rather than from hand-edited defaults on individual hosts, so recovery and rebuilds land in a known target every time.

## The runlevel model SysV init modelled system state as a single small integer. The machine was in exactly one runlevel at a time, conventionally: 0 halt, 1 single-user, 2–4 multi-user variants, 5 multi-user with a graphical login, 6 reboot. The default was declared once in `/etc/inittab` with a line of the form `id:3:initdefault:`, and `telinit 5` moved the running system from one number to another. Because the state was a scalar, every transition meant stopping the services that belonged to the old level and starting those that belonged to the new one. ## What a target unit is systemd has no scalar state at all. A target is simply a unit — like a service unit or a mount unit — of type `.target`. It starts no process and has no executable behind it. Its entire purpose is twofold: 1. **Grouping.** Other units are hooked under it, so activating the target activates them. 2. **Synchronisation.** It is a named milestone other units can order themselves around: "not before basic services exist", "not before the network is configured". Because a target is an ordinary unit, everything that works for units works for targets: you can start one, check its status, list what it pulls in, and depend on it. ## Several targets are active at once This is the sharpest break from runlevels. Targets form a graph, not a ladder. `graphical.target` pulls in `multi-user.target`, which pulls in `basic.target`, which pulls in `sysinit.target`. On a booted desktop all four are active simultaneously. Asking "which target is the system in?" is therefore not quite the right question — a system is in *many* targets. The closest analogue to the old question is "which target did boot aim for?", answered by `default.target`. ```bash systemctl list-units --type=target ``` lists them all, and you will see far more than one. ## Ordering is derived, not numbered Under SysV, order came from the two-digit prefix a human (or a helper tool) chose for each symlink, and the scripts ran strictly one after another. Under systemd, each unit declares its own relationships and systemd computes a dependency graph, then starts everything whose prerequisites are satisfied in parallel. Two consequences follow: boot is dramatically faster on a machine with many services, and "just bump the number" is no longer how you fix ordering — you express the actual relationship instead. (The syntax for expressing those relationships belongs to systemd unit-file study; what matters here is the model: derived graph rather than assigned sequence.) ## default.target replaces initdefault The boot goal is a symlink: ```bash systemctl get-default # e.g. multi-user.target sudo systemctl set-default graphical.target ``` `set-default` rewrites `/etc/systemd/system/default.target` to point at the chosen target. That single symlink is the whole of what `initdefault` used to express. ## The compatibility aliases To ease migration, systemd ships alias units so old habits and old scripts keep working: - `runlevel0.target` → `poweroff.target` - `runlevel1.target` → `rescue.target` - `runlevel2.target`, `runlevel3.target`, `runlevel4.target` → `multi-user.target` - `runlevel5.target` → `graphical.target` - `runlevel6.target` → `reboot.target` Note the collapse: three distinct runlevels map onto one target, because the historical distinction between runlevels 2, 3 and 4 was purely conventional and differed by distribution anyway. The `runlevel` command still exists and reports a plausible answer, but it is an emulation, not a reading of real state. ## isolate is the closest thing to telinit `systemctl start multi-user.target` activates that target *in addition to* whatever is already running — it stops nothing. `systemctl isolate multi-user.target` activates it and stops every unit not required by it, which is the behaviour `telinit 3` had. Isolation is deliberately the less convenient spelling, because abruptly stopping unrelated units is rarely what you want on a production host. ## Why interviewers ask this The question separates vocabulary translation from model understanding. Anyone can memorise "runlevel 3 equals multi-user.target". The useful answer explains that the underlying model changed: from one exclusive scalar advanced by numbered scripts, to a dependency graph of named units of which many are active at once.

  • Why does `systemctl start multi-user.target` behave differently from the old `telinit 3`?
    `start` only activates the target and everything it pulls in; anything already running that the target does not require keeps running. `telinit 3` also tore down whatever belonged to the previous runlevel. The equivalent is `systemctl isolate multi-user.target`, which activates the target and stops units not required by it. Isolation is the explicit spelling precisely because it is disruptive.
  • Three different runlevels map onto multi-user.target. Why was that collapse acceptable?
    Runlevels 2, 3 and 4 never had fixed cross-distribution meanings; only 0, 1, 5 and 6 were reliably conventional. Debian, for instance, treated 2 through 5 as identical multi-user levels while Red Hat used 3 for text and 5 for graphical. Since the distinction carried no portable semantics, collapsing them onto a single target lost nothing real.
  • How would you find out what a target actually pulls in on a given host?
    Ask systemd rather than guessing, since the set is host- and package-specific. `systemctl list-dependencies multi-user.target` walks the graph, and listing the target's `.wants` directories under `/etc/systemd/system/` and the vendor unit directory shows what enablement has hooked in. The answer differs between distributions and between two hosts of the same distribution.

saying these in an interview costs you the question

  • Says a target is just a renamed runlevel
  • Claims exactly one target is active at a time
  • Thinks targets start in numeric order like rc scripts
  • Confuses set-default with isolate
  • Believes runlevel 3 has a fixed meaning on every distribution

context