Systemd Targets Explained: Managing Boot States and Runlevels Effectively

Understand systemd targets, runlevel equivalents, default boot states, isolate, rescue, and graphical modes.

Systemd Targets Explained: Managing Boot States and Runlevels Effectively

Systemd targets are named system states. A target can represent a normal server boot, a graphical desktop, a rescue shell, a shutdown, or a custom group of services you want to start together. If you came from older Linux systems, targets are the systemd replacement for the parts of runlevels that people actually used, but they are more flexible because they are built from dependencies instead of only numbers.

You usually meet targets when a server boots into the wrong mode, a desktop display manager will not start, or a recovery guide tells you to enter rescue.target. Knowing a few commands makes those situations much less mysterious.

The Evolution from Runlevels to Systemd Targets

Historically, Linux systems used a concept called runlevels to define the system's operating state during boot and runtime. A runlevel was a numerical identifier (0-6) that dictated which services were started or stopped. For example, runlevel 3 typically meant a multi-user text mode, while runlevel 5 indicated a graphical multi-user environment. This system, while functional, had limitations:

  • Rigidity: Runlevels were often defined in a fixed or distribution-specific way, making it awkward to customize the exact set of services active for a given state.
  • Implicit Dependencies: Dependencies between services were often managed indirectly through runlevel assignments, leading to potential conflicts or missed services.
  • Lack of Granularity: The numerical system lacked descriptive clarity, making it harder to understand the intended state of the system.

Systemd targets address these limitations by providing a more explicit, dependency-driven, and descriptive approach. Instead of abstract numbers, targets have meaningful names (e.g., multi-user.target, graphical.target) that clearly indicate the intended system state. Dependencies are explicitly defined within unit files, ensuring that all necessary components are started in the correct order.

The rough mapping looks like this on many systemd systems:

Traditional runlevel Common systemd equivalent Meaning
0 poweroff.target Shut down and power off
1 rescue.target Single-user rescue mode
3 multi-user.target Multi-user text/server mode
5 graphical.target Multi-user mode plus graphical login
6 reboot.target Reboot

Treat this as a translation aid, not a law. Runlevel behavior varied across distributions, and systemd targets can express relationships that old runlevels could not.

Understanding Systemd Targets

A systemd target is itself a type of unit. When a target unit is activated, systemd attempts to activate all the units that are listed as dependencies within that target's unit file. This creates a cascading effect, ensuring that all necessary services, devices, and other components are brought online to reach the desired system state.

Key Characteristics of Systemd Targets:

  • Dependency Management: Targets define what other units need to be active for the target to be considered reached. This is the core of their power.
  • Synchronization Points: They act as synchronization points during the boot process. The system won't proceed to the next stage until the current target is fully initialized.
  • Descriptive Naming: Targets are named descriptively, making it easy to understand the system's intended state (e.g., rescue.target, poweroff.target).

Targets usually do not run long-lived code themselves. They group other units. You can inspect that grouping with:

systemctl list-dependencies multi-user.target
systemctl list-dependencies graphical.target

This is a good way to answer, "Why does this service start at boot?" If the unit appears in the dependency tree for the default target, it is being pulled in somewhere.

Common Systemd Targets

Systemd comes with a set of predefined targets designed to cover common system states. Understanding these is key to managing your system.

multi-user.target

This is one of the most fundamental targets. It represents a fully functional, multi-user system with networking enabled but without a graphical login manager or desktop environment. This is typically the default target for servers.

  • Purpose: To provide a stable environment for running services and allowing multiple users to log in via text-based consoles or SSH.
  • Dependencies: Usually includes units for networking, system services, and console login prompts.

For headless servers, multi-user.target is usually the right default. It gives you SSH and normal services without spending resources on a display manager.

graphical.target

This target represents a fully functional, multi-user system with a graphical desktop environment ready for user interaction. It is typically a dependent of multi-user.target and adds the necessary components for a graphical session.

  • Purpose: To launch a graphical display manager (like GDM, LightDM, SDDM) and the associated desktop environment.
  • Dependencies: Commonly pulls in multi-user.target behavior and adds units for a display manager and graphical session components.

If a workstation boots to a black screen but SSH still works, compare these:

systemctl get-default
systemctl status display-manager.service
journalctl -u display-manager.service -b

The default target tells you what the system tried to reach. The display manager logs tell you why the graphical layer did or did not come up.

rescue.target

This target provides a minimal, single-user environment. It's primarily used for system maintenance and recovery. It brings up the basic system and a root shell but typically doesn't start networking or multi-user services.

  • Purpose: To provide a safe environment for system administrators to perform maintenance tasks without interference from other services.
  • Dependencies: Minimal set of essential system components and a root shell. Networking is often not available unless you start it manually.

emergency.target

This is even more minimal than rescue.target. It brings the system up to a single read-only filesystem and a root shell. It's intended for dire emergency situations where even basic services might be problematic.

  • Purpose: For critical system recovery when even the rescue.target might not be appropriate.
  • Dependencies: Only the most essential system components and a root shell. The root filesystem may be mounted read-only depending on the failure and distribution.

reboot.target, poweroff.target, halt.target

These are special targets used to shut down or restart the system. When systemd activates one of these targets, it stops all running services and then performs the specified action (rebooting, powering off, or halting).

  • Purpose: To gracefully shut down or restart the system.
  • Dependencies: They typically depend on services that need to be stopped before the system can be shut down.

Managing Systemd Targets

Systemd provides several command-line tools to interact with targets. The primary tool is systemctl.

Viewing Current and Default Targets

To see which target the system is currently running and which target it defaults to on boot, use:

systemctl status

This command provides a wealth of information, including the active target. To specifically query the default target:

systemctl get-default

To see all available targets:

systemctl list-unit-files --type=target

To see active target units, use:

systemctl list-units --type=target

The difference is the same as with services: list-unit-files shows target files systemd knows about, while list-units shows targets currently loaded or active in the running system.

Changing the Default Target

If you want your system to boot into a different target by default (e.g., from graphical to multi-user, or vice-versa), you can use systemctl set-default:

To set the default to graphical target (common for desktop systems):

sudo systemctl set-default graphical.target

To set the default to multi-user target (common for servers):

sudo systemctl set-default multi-user.target

Important: Changing the default target will only take effect on the next reboot.

Under the hood, this changes the default.target symlink. You can inspect it directly if you are debugging a broken boot image:

systemctl get-default
ls -l /etc/systemd/system/default.target

Switching to a Target (Without Rebooting)

You can switch the system to a different target immediately without rebooting. This is useful for testing or temporarily changing the system's state. Use the systemctl isolate command:

To switch to the graphical target:

sudo systemctl isolate graphical.target

To switch to the multi-user target:

sudo systemctl isolate multi-user.target

Caution: systemctl isolate is a powerful command. Isolating to a target like rescue.target or emergency.target will stop most running services. Ensure you understand the implications before using it. You might lose network connectivity or your graphical session.

On a remote server, be especially careful with isolate rescue.target or isolate emergency.target. You may lose SSH and need console access through your cloud provider, hypervisor, or physical machine. If you only need to stop the graphical desktop on a workstation, isolating to multi-user.target is less drastic than jumping straight into rescue mode.

How Targets Relate to Unit Files

Targets are implemented as unit files, typically located in /usr/lib/systemd/system/ or /etc/systemd/system/. A target unit file (e.g., graphical.target) specifies dependencies on other units, including other targets and services.

A typical graphical.target unit file might look something like this (simplified):

[Unit]
Description=Graphical multi-user system
Documentation=man:systemd.special(7)
# This target is intended to be a prerequisite for the graphical login manager.
# It's the target that the system will boot into if not otherwise specified.
Wants=display-manager.service
Before=shutdown.target

[Install]
Alias=default.target

Here:

  • Wants=display-manager.service: Indicates that display-manager.service (the actual login manager like GDM or LightDM) should be started if possible. This is a weaker dependency than Requires=.
  • Before=shutdown.target: Ensures that the graphical environment is stopped before the system enters the shutdown process.
  • Alias=default.target: This makes graphical.target act as the default if default.target is linked to it (which it usually is for desktop systems).

Creating Custom Targets

While less common for day-to-day use, you can create your own custom targets to define specific system states with unique sets of services.

Steps to Create a Custom Target:

  1. Create a .target unit file: Place it in /etc/systemd/system/ (e.g., my-custom.target).
    [Unit]
    Description=My Custom Target
    
    [Install]
    WantedBy=multi-user.target # Or another appropriate target
    
  2. Create .service or other unit files: Define the services and other units that should be active for your custom target.
  3. Add dependencies: In your custom target's unit file, use Requires= or Wants= to specify which units must or should be started.
    [Unit]
    Description=My Custom Target
    Wants=service1.service
    Wants=service2.service
    After=service1.service service2.service
    
    [Install]
    WantedBy=multi-user.target
    
  4. Reload systemd:
    
    

sudo systemctl daemon-reload 5. **Enable/Start your target:** bash sudo systemctl start my-custom.target # Or to make it bootable sudo systemctl enable my-custom.target ```

Use Case: Imagine a development environment where you need specific database and application servers running. You could create a dev-env.target that starts these services.

Custom targets are also useful for appliance-like systems. For example, a kiosk machine might have a target that starts networking, a browser, a local watchdog, and a logging agent, but not the rest of a normal desktop session. A lab server might have separate targets for a test stack and a demo stack so operators can start a known group of services together.

Best Practices and Tips

  • Understand the Default: Know your system's default target (graphical.target or multi-user.target) as it dictates the initial boot experience.
  • Use isolate with Caution: Be mindful when using systemctl isolate, especially on production systems, as it can disrupt running services.
  • Check Dependencies: If a service isn't starting, examine the dependencies of the target it's associated with using systemctl list-dependencies <target_name>.
  • Server vs. Desktop: On servers, multi-user.target is almost always preferred for security and resource efficiency. On desktops, graphical.target is standard.
  • System Maintenance: For tasks requiring minimal interference, rescue.target is useful. For critical recovery, emergency.target is available.

A Practical Way to Think About Targets

For most administration work, you only need a short mental model:

systemctl get-default
systemctl set-default multi-user.target
systemctl isolate graphical.target
systemctl list-dependencies graphical.target

get-default tells you where the machine is supposed to boot. set-default changes the next boot. isolate changes the current state and may stop services outside that state. list-dependencies explains what a target pulls in.

Targets are not just renamed runlevels. They are dependency groups. Once you treat them that way, boot problems become easier to reason about: find the target the system tried to reach, inspect the services it wanted, then fix the unit or dependency that failed.