Systemd Architecture: Units, Targets, and Dependencies
Systemd architecture explains how many Linux systems start services, coordinate dependencies, and supervise processes. It is often discussed through commands such as systemctl, but those commands control a larger manager built around unit objects and a dependency graph. Understanding that model helps administrators diagnose slow boots and failed services, and helps developers decide how their applications should start and recover.
What Is systemd?
Systemd is a system and service manager for Linux. On distributions that use it as their init system, the kernel starts systemd as process ID 1 after early userspace has been prepared. The system manager then brings the machine into its configured operating state and supervises system services. A separate systemd user manager can manage services belonging to a logged-in user.
The systemd project is a collection of system components, but the central architecture is the manager and the units it loads. A unit is a named object describing a resource or action, such as a service process, a mount, a timer, or a group of other units. The official systemd.unit reference describes shared unit-file syntax, dependencies, and load behavior.
Systemd is not the Linux kernel, and it does not replace the kernel’s process scheduler. It asks the kernel to start processes and uses kernel facilities such as control groups to track and control them. It is also not universal: some Linux distributions use other init systems, and systemd may run inside a container without being the host’s process 1.
The Problem Systemd Solves
Starting a useful Linux system is a coordination problem. Filesystems must be mounted before applications use them; device or network services may need to appear before dependent workloads; and long-running daemons need supervision, restart policy, and useful status information. A simple sequence of shell commands can start programs, but it does not inherently describe these relationships or recover cleanly when part of the sequence fails.
Older Unix-like systems commonly relied on SysV-style init scripts and runlevels. The Linux Standard Base init-script specification documents conventions for those scripts. Systemd can accommodate some legacy services, but native units express dependencies and ordering directly and can be activated by events as well as during boot.
The key distinction is that systemd builds a desired set of work from units and their relationships. It can schedule independent jobs concurrently, order jobs that depend on one another, and report which unit failed. This makes startup more declarative than a single fixed script sequence, although poorly described dependencies can still cause failures or delays.
How Systemd Works
The system manager loads unit definitions from vendor, administrator, and runtime locations, applies drop-ins and generators, and resolves unit names into a job graph. When asked to start a unit or reach a target, it queues the work implied by that request and its dependencies. Jobs with no ordering relationship can often proceed in parallel; explicit ordering constrains only the jobs that need it.
Dependencies and ordering are separate concepts. Wants= and Requires= express that another unit should be pulled in; After= and Before= express the order in which jobs run. For example, Wants=network-online.target requests that target, but does not by itself make the current unit wait for it. Adding After=network-online.target establishes ordering, while the exact meaning of “online” still depends on the network manager and its wait-online service.
Targets provide named synchronization points and convenient groups of units. At boot, the manager follows the configured default.target, which commonly points to a target such as multi-user.target or graphical.target. The systemd.target documentation defines target units as a way to group units and establish ordering. A target does not launch a process of its own; reaching it means its requested jobs have been handled according to their dependencies and ordering.
After a service starts, systemd tracks its process and applies the service unit’s settings. On Linux, control groups let the manager associate processes with a service and apply resource controls to the group. If a process exits, the configured service type and restart policy determine whether the unit is considered complete, failed, or eligible for another start. The systemd.service reference describes service-specific behavior.
Activation need not wait for boot. A socket unit can listen for connections and start its corresponding service when a request arrives; a timer can activate a service at a scheduled time; and path or device units can respond to other changes. This lets a machine defer work until it is needed, rather than starting every service immediately.
Components and Key Concepts
Unit names usually include a type suffix. The suffix determines what kind of object the manager loads, while dependencies connect units into larger workflows.
| Unit type | Represents | Typical role |
|---|---|---|
.service |
A process or one-shot command | Start, supervise, and stop a daemon or task |
.target |
A synchronization point or group | Collect units for boot states or application stacks |
.socket |
A listening socket | Activate a service when a connection arrives |
.timer |
A time-based trigger | Schedule a matching service unit |
.mount |
A filesystem mount | Describe and order a mount operation |
.path |
A filesystem path to monitor | Activate a service when a path changes |
Most native unit files have a [Unit] section for metadata and relationships, then a type-specific section such as [Service] or [Timer]. An [Install] section describes how commands such as systemctl enable should connect the unit to other units. It is not a set of runtime service instructions: enabling a unit generally creates the appropriate symlink, so it can be pulled in by a target later.
Targets are related to, but not simply identical with, SysV runlevels. Names such as rescue.target and multi-user.target represent operating states using unit relationships, and compatibility aliases exist for familiar runlevel names. The systemd target reference is the authoritative guide to target behavior; the LSB document describes the older init-script convention, not a standard for native systemd units.
Systemd also includes companion services such as systemd-journald for collecting logs. The system manager handles unit lifecycle and job coordination; the journal stores and exposes records. Keeping those roles distinct helps when troubleshooting: systemctl status reports unit state, while journalctl queries log entries.
Real-World Uses
During boot, targets express broad milestones such as a rescue environment, a multi-user server, or a graphical session. Filesystem, device, and service units provide the work required to reach those milestones. The dependency graph lets unrelated services start concurrently without requiring every distribution to maintain one rigid startup script.
For server applications, service units provide a repeatable way to define the executable, environment, user, restart behavior, and resource controls. A socket-activated service can stay idle until a client connects. On a workstation or server, timer units can schedule maintenance and pair each run with service-level logs and resource settings.
The architecture also matters in containers and automation. Container runtimes use Linux namespaces and control groups to isolate processes and manage resources, as described in our Linux container security guide. Systemd units can define process and resource behavior inside a systemd-based environment, but a container does not automatically need a full init system; the runtime and workload determine whether one is appropriate.
Getting Started: Inspecting Units and Targets
Begin with read-only inspection on a machine that runs systemd. These commands show the configured default target, the targets currently loaded, and the units pulled in by a target:
systemctl get-default
systemctl list-units --type=target --all
systemctl list-dependencies multi-user.target
systemctl status systemd-journald.service
systemctl cat systemd-journald.service
Use systemctl cat to see the main unit file and any drop-ins. To inspect dependency and ordering properties, use:
systemctl show systemd-journald.service \
--property=Requires,Wants,After,Before
systemd-analyze critical-chain
The critical-chain command is useful for understanding ordered boot delays, but it is not a complete performance profile: units that run in parallel may not all appear on the critical path.
A custom target can group application services. On a test machine, place it in the administrator’s system-unit directory:
/etc/systemd/system/app-stack.target
Replace the example service names below with installed units:
[Unit]
Description=Application workload group
Requires=api.service worker.service
After=api.service worker.service
Requires= pulls in both services and makes the target depend on them; After= orders the target after their start jobs. Neither line configures the services themselves. For optional supporting units, Wants= can be used instead of Requires=, so their failure does not necessarily prevent the target from being reached.
Validate and try the target in a disposable test environment:
sudo systemd-analyze verify /etc/systemd/system/app-stack.target
sudo systemctl daemon-reload
sudo systemctl start app-stack.target
systemctl list-dependencies app-stack.target
systemctl status app-stack.target
daemon-reload makes the manager reread unit definitions; it does not restart running services by itself. Starting this target also does not make it a boot default. Enabling a target or changing the default boot target is a separate administrative decision. A target is not automatically a stop-propagation mechanism either: stopping it does not, by itself, guarantee that every unit it started is stopped.
When troubleshooting, check the failed unit and its logs rather than guessing from the target name:
systemctl --failed
systemctl status api.service
journalctl -u api.service -b --no-pager
For day-to-day service operations, see our practical guide to managing systemd services. If the job is scheduled rather than continuously supervised, compare the cron and systemd timer models.
Common Misconceptions
“A target is a script that runs every service in order.”
A target is a unit that groups other units and provides a synchronization point. Dependencies determine which jobs are pulled in, and ordering directives constrain their sequence. Independent jobs may run at the same time.
“After= starts the named unit.”
After= controls ordering only. To request another unit, use a dependency such as Wants= or Requires= as appropriate. Conversely, a dependency does not necessarily establish order unless an ordering relationship is also present.
“Enabling a service starts it immediately.”
Enabling normally configures how a unit is pulled in later, often at boot. Starting requests activation now. systemctl enable --now intentionally performs both actions; the distinction is useful when changing boot behavior on a production system.
“Systemd replaces the kernel or every traditional init system.”
Systemd is userspace software that relies on kernel interfaces, and not every Linux distribution adopts it. It supports some compatibility with older service conventions, but native unit files are systemd-specific rather than a cross-platform init standard.
Related Articles
- Managing systemd Services
- Scheduling Tasks on Linux: Cron vs. systemd Timers
- Linux Container Security
Changelog
- Initial publication: Explains systemd’s unit model, target relationships, activation, and service inspection.

