What Is SystemD and How Does It Boot Your Linux System?

systemd is the init system and service manager used by most modern Linux distributions including Ubuntu, Fedora, Debian, and Arch Linux. When your computer boots, the Linux kernel starts systemd as PID 1 — the first process — which then starts all other services and processes in parallel. You interact with systemd through the systemctl command to start, stop, enable, disable, and check the status of services, and through journalctl to read system logs. systemd replaced the older SysV init system and dramatically improved boot times through parallel service startup.

The First Process That Starts Everything

When you press the power button on a Linux computer, a precisely ordered sequence of events begins. The hardware initializes, the BIOS or UEFI firmware runs, the bootloader (GRUB) loads the Linux kernel, and the kernel initializes itself and the hardware. But then what? The kernel needs to hand control to something that will start all the services, daemons, and processes that make the system usable.

That something is the init system — the first process started by the kernel, always assigned PID 1. The init system is the ancestor of every other process on the system and is responsible for starting everything, monitoring running services, and managing system state changes (shutdown, reboot, entering maintenance mode).

For decades, Linux used the SysV init system — a simple but sequential approach where services started one after another based on numbered scripts in /etc/rcX.d/ directories. SysV init worked but had significant limitations: boot was slow (sequential startup cannot overlap independent services), dependency management was crude, no unified logging, and managing the system required learning dozens of different service-specific scripts.

In 2010, Lennart Poettering announced systemd — a comprehensive reimagining of the init system. systemd starts services in parallel where possible, tracks processes with cgroups, provides unified logging through the journal, manages network, time synchronization, and dozens of other system functions, and offers a declarative unit file format that describes services rather than requiring Bash scripts for each one.

systemd is now the default init system on Ubuntu (since 15.04), Fedora (since version 15), Debian (since version 8), Arch Linux, openSUSE, and virtually every mainstream Linux distribution. It is simultaneously one of the most important pieces of software on modern Linux systems and one of the most controversial — it has attracted strong opinions about scope, complexity, and design philosophy. Whatever your opinion of systemd’s design, understanding how it works and how to use it effectively is an essential skill for any Linux user or administrator.

What systemd Does

systemd is not just an init system — it is a suite of tightly integrated components:

PID 1 (init) — the first process, ancestor of all others, responsible for starting the system.

Service manager — starts, stops, monitors, and restarts services and daemons.

Dependency resolver — understands relationships between services and starts them in the right order and parallelism.

Logging (journald) — collects logs from all services and the kernel in a centralized, structured binary journal.

Device management (udevd) — manages device nodes in /dev when hardware is added or removed.

Login manager (logind) — manages user sessions, seat management, and power management for desktop systems.

Network management (networkd) — optional network configuration manager for servers.

Time synchronization (timesyncd) — keeps the system clock synchronized.

DNS resolution (resolved) — local DNS caching resolver.

Boot management — measures and analyzes boot performance, manages boot options.

This breadth is why systemd is controversial — critics argue it violates Unix’s “do one thing well” principle. Defenders argue the integration provides reliability and features that separate init + logging + udev systems could not achieve. The practical reality: on the Linux systems you use daily, systemd is managing all of this, and understanding it makes you more capable.

The systemd Boot Process

Understanding the boot sequence from kernel to usable system clarifies why systemd exists and what it does.

Stage 1: Kernel Initialization

After the bootloader hands control to the Linux kernel:

  1. The kernel decompresses itself
  2. Initializes hardware subsystems (memory, CPU, buses)
  3. Mounts the root filesystem (initially a temporary initramfs if needed)
  4. Starts PID 1 — by default /sbin/init, which on modern systems is /usr/lib/systemd/systemd

Stage 2: systemd Takes Over

Once systemd starts as PID 1:

  1. Reads its configuration — system configuration in /etc/systemd/system/ and defaults in /usr/lib/systemd/system/
  2. Determines the default target — systemd uses targets (analogous to runlevels) to define what state the system should reach. The default target for a desktop system is graphical.target; for a server it might be multi-user.target.
  3. Resolves dependencies — systemd builds a dependency graph. To reach graphical.target, systemd determines every service and unit that needs to start and in what order.
  4. Starts units in parallel — unlike SysV init’s sequential startup, systemd starts independent units simultaneously. Services that depend on others wait only for their specific dependencies, not for unrelated services.
  5. Monitors startup — systemd tracks which units started successfully, which failed, and which are still starting.

Stage 3: Reaching the Target

As services start:

  • systemd-journald starts early to capture logs from the beginning
  • systemd-udevd starts and processes hardware events
  • Network configuration (NetworkManager or systemd-networkd) starts
  • Time synchronization (systemd-timesyncd) contacts NTP servers
  • The display manager (gdm, sddm, lightdm) starts, presenting the login screen
  • After login, the user session manager starts the desktop environment

The entire sequence from kernel start to login screen typically takes 10–30 seconds on modern hardware — much faster than the SysV init sequential approach could achieve.

Viewing Boot Performance

$ systemd-analyze
Startup finished in 5.291s (firmware) + 1.892s (loader) + 2.107s (kernel) + 12.453s (userspace) = 21.743s
graphical.target reached after 12.228s in userspace

$ systemd-analyze blame
          5.432s NetworkManager-wait-online.service
          3.287s apt-daily-upgrade.service
          2.156s snapd.service
          1.893s plymouth-quit-wait.service
          1.245s dev-sda2.device

systemd-analyze blame shows which services took longest to start — invaluable for diagnosing slow boots.

$ systemd-analyze critical-chain
graphical.target @12.228s
└─gdm.service @11.892s +335ms
  └─network.target @11.849s
    └─NetworkManager.service @3.421s +8.415s
      └─dbus.service @3.388s +27ms
        └─sysinit.target @3.356s

The critical chain shows the longest dependency path to reaching the graphical target — the services that could not be parallelized.

Units: The Building Blocks of systemd

Everything systemd manages is described by a unit — a configuration file that describes a resource, service, device, mount point, timer, or other system component.

Unit Types

Extension Unit Type What It Manages
.service Service A daemon or background process
.timer Timer A scheduled task (systemd’s cron alternative)
.target Target A group of units / system state milestone
.socket Socket Network socket or IPC endpoint
.device Device A hardware device
.mount Mount A filesystem mount point
.automount Automount On-demand filesystem mounting
.path Path File/directory monitoring
.slice Slice A cgroup resource management group
.scope Scope An externally created process group
.swap Swap A swap device or file

Where Unit Files Live

/usr/lib/systemd/system/    ← Unit files installed by packages (do not edit)
/etc/systemd/system/        ← Administrator customizations (override or add units)
/run/systemd/system/        ← Runtime unit files (transient, cleared on reboot)
~/.config/systemd/user/     ← Per-user unit files

The priority order: /etc/ overrides /run/ which overrides /usr/lib/. To customize a system unit, copy it to /etc/systemd/system/ and edit the copy — your version takes precedence over the package-installed version.

Anatomy of a Service Unit File

A typical service unit file:

$ cat /usr/lib/systemd/system/nginx.service
[Unit]
Description=A high performance web server and a reverse proxy server
Documentation=man:nginx(8)
After=network.target nss-lookup.target

[Service]
Type=forking
PIDFile=/run/nginx.pid
ExecStartPre=/usr/sbin/nginx -t -q -g 'daemon on; master_process on;'
ExecStart=/usr/sbin/nginx -g 'daemon on; master_process on;'
ExecReload=/bin/kill -s HUP $MAINPID
ExecStop=/bin/kill -s QUIT $MAINPID
PrivateTmp=true

[Install]
WantedBy=multi-user.target

[Unit] section:

  • Description — human-readable name shown in status output
  • Documentation — where to find docs (man page, URL)
  • After — start this unit after the listed units (ordering, not dependency)
  • Requires — this unit requires the listed units to be active (hard dependency)
  • Wants — weaker dependency: listed units should start but failure is acceptable

[Service] section:

  • Type — how the service is expected to behave:
    • simple — the ExecStart command is the main process (default)
    • forking — the service forks and the parent exits (traditional daemon behavior)
    • oneshot — runs once and exits (like a startup script)
    • notify — service sends a readiness notification when it is fully started
    • dbus — service registers on D-Bus when ready
  • ExecStart — the command to start the service
  • ExecStop — how to stop the service
  • ExecReload — how to reload configuration without restarting
  • Restart — when to automatically restart: on-failure, always, on-abnormal
  • RestartSec — seconds to wait before restarting
  • User / Group — run as this user/group
  • PrivateTmp — give the service its own private /tmp

[Install] section:

  • WantedBy — which target should “want” this unit (determines when it starts)
  • RequiredBy — like WantedBy but with a hard dependency

Targets: System State Milestones

Targets group units into meaningful milestones and replace SysV init’s runlevels:

Target SysV Equivalent Description
poweroff.target Runlevel 0 System shutdown
rescue.target Runlevel 1 Single-user rescue mode
multi-user.target Runlevel 3 Multi-user, no GUI
graphical.target Runlevel 5 Multi-user with GUI
reboot.target Runlevel 6 System reboot
emergency.target — Emergency shell (minimal)

Checking and Changing the Default Target

$ systemctl get-default
graphical.target

$ sudo systemctl set-default multi-user.target    # Boot to text mode by default
$ sudo systemctl set-default graphical.target      # Boot to GUI by default

Switching Targets Without Rebooting

$ sudo systemctl isolate multi-user.target    # Switch to text mode now
$ sudo systemctl isolate graphical.target     # Switch back to GUI
$ sudo systemctl rescue                       # Enter rescue mode

systemctl: Managing Services

systemctl is the primary command for interacting with systemd.

Starting, Stopping, and Restarting Services

$ sudo systemctl start nginx           # Start a service now
$ sudo systemctl stop nginx            # Stop a service now
$ sudo systemctl restart nginx         # Stop and start
$ sudo systemctl reload nginx          # Reload config without full restart (if supported)
$ sudo systemctl reload-or-restart nginx  # Reload if supported, otherwise restart

Enabling and Disabling Services

“Enabling” a service means it will start automatically at boot:

$ sudo systemctl enable nginx          # Start at boot
$ sudo systemctl disable nginx         # Don't start at boot
$ sudo systemctl enable --now nginx    # Enable AND start immediately
$ sudo systemctl disable --now nginx   # Disable AND stop immediately

Enabling creates a symlink in the appropriate target directory:

$ ls /etc/systemd/system/multi-user.target.wants/
nginx.service  ssh.service  cron.service  ...

Checking Service Status

$ systemctl status nginx
● nginx.service - A high performance web server and a reverse proxy server
     Loaded: loaded (/lib/systemd/system/nginx.service; enabled; vendor preset: enabled)
     Active: active (running) since Mon 2026-02-18 09:00:12 UTC; 2h 15min ago
       Docs: man:nginx(8)
    Process: 1234 ExecStartPre=/usr/sbin/nginx -t -q -g daemon on; master_process on; (code=exited, status=0/SUCCESS)
   Main PID: 1235 (nginx)
      Tasks: 5 (limit: 18884)
     Memory: 12.4M
        CPU: 243ms
     CGroup: /system.slice/nginx.service
             ├─1235 "nginx: master process /usr/sbin/nginx -g daemon on; master_process on;"
             ├─1236 "nginx: worker process"
             └─1237 "nginx: worker process"

Feb 18 09:00:12 myserver systemd[1]: Starting A high performance web server...
Feb 18 09:00:12 myserver systemd[1]: Started A high performance web server.

The status output shows:

  • Whether the service is loaded and whether it is enabled
  • Current active state (running, stopped, failed)
  • Main process ID and child processes
  • Resource usage (memory, CPU)
  • CGroup the service runs in
  • Recent log entries

Listing Services

$ systemctl list-units --type=service             # All active service units
$ systemctl list-units --type=service --all       # All service units (including inactive)
$ systemctl list-units --state=failed             # Only failed units
$ systemctl list-unit-files --type=service        # All service files with enabled/disabled state

Masking a Service

Masking prevents a service from being started by anything — even manually:

$ sudo systemctl mask bluetooth.service    # Completely prevent bluetooth from starting
$ sudo systemctl unmask bluetooth.service  # Re-enable it

Masking creates a symlink pointing to /dev/null, which overrides the unit file. Even systemctl start bluetooth fails when masked.

journalctl: Reading System Logs

systemd’s journal (journald) collects logs from all services, the kernel, and user sessions in a centralized, structured binary format. journalctl queries this journal.

Basic Log Viewing

$ journalctl                          # All logs (oldest first — press G to jump to end)
$ journalctl -r                       # Reverse order (newest first)
$ journalctl -f                       # Follow (like tail -f) — shows new entries in real time
$ journalctl -n 50                    # Last 50 lines
$ journalctl -n 50 -r                 # Last 50 lines in reverse

Filtering Logs

By service/unit:

$ journalctl -u nginx.service         # Logs for nginx only
$ journalctl -u nginx -u mysql        # Logs for nginx and mysql
$ journalctl -u nginx -f              # Follow nginx logs

By time:

$ journalctl --since "2026-02-18 09:00:00"
$ journalctl --since "1 hour ago"
$ journalctl --since today
$ journalctl --since "2026-02-18" --until "2026-02-19"

By priority/severity:

$ journalctl -p err                   # Only errors and above
$ journalctl -p warning               # Warnings and above
$ journalctl -p 0..3                  # Emergency through error

Priority levels: 0=emerg, 1=alert, 2=crit, 3=err, 4=warning, 5=notice, 6=info, 7=debug

By boot:

$ journalctl -b                       # This boot's logs
$ journalctl -b -1                    # Previous boot's logs
$ journalctl -b -2                    # Two boots ago
$ journalctl --list-boots             # Show all available boot records

Kernel messages:

$ journalctl -k                       # Kernel messages (like dmesg)
$ journalctl -k -b -1                 # Previous boot's kernel messages

By process:

$ journalctl _PID=1234
$ journalctl _UID=1000                # Messages from user with UID 1000

Output Formats

$ journalctl -u nginx -o json         # JSON output
$ journalctl -u nginx -o json-pretty  # Pretty-printed JSON
$ journalctl -u nginx -o cat          # Just the message, no metadata
$ journalctl -u nginx -o short-iso    # ISO 8601 timestamps

Checking Journal Disk Usage

$ journalctl --disk-usage
Archived and active journals take up 1.2G in the file system.

Cleaning Old Logs

$ sudo journalctl --vacuum-size=500M    # Keep only 500M of logs
$ sudo journalctl --vacuum-time=2weeks  # Delete logs older than 2 weeks

Creating a Custom systemd Service

Writing your own service unit file is straightforward once you understand the format.

Example: Running a Python Application as a Service

You have a Python web application at /opt/myapp/app.py that should run automatically at boot as the webapp user:

Create the service file:

$ sudo nano /etc/systemd/system/myapp.service
[Unit]
Description=My Python Web Application
After=network.target
Wants=network.target

[Service]
Type=simple
User=webapp
Group=webapp
WorkingDirectory=/opt/myapp
ExecStart=/usr/bin/python3 /opt/myapp/app.py
Restart=on-failure
RestartSec=5
StandardOutput=journal
StandardError=journal

# Security hardening
NoNewPrivileges=yes
PrivateTmp=yes
ProtectSystem=strict
ReadWritePaths=/opt/myapp/data

[Install]
WantedBy=multi-user.target

Reload systemd to recognize the new unit:

$ sudo systemctl daemon-reload

Enable and start:

$ sudo systemctl enable --now myapp.service

Verify:

$ systemctl status myapp.service
$ journalctl -u myapp.service -f

Overriding Package Unit Files

To modify a service installed by a package without editing the package’s file (which would be overwritten on update):

$ sudo systemctl edit nginx.service

This creates a drop-in override file at /etc/systemd/system/nginx.service.d/override.conf. Add only the settings you want to change:

[Service]
LimitNOFILE=65536
Restart=always
RestartSec=3

The drop-in is merged with the original unit file — your settings override specific values while leaving everything else unchanged.

Common systemctl Tasks Quick Reference

Task Command
Start a service sudo systemctl start service_name
Stop a service sudo systemctl stop service_name
Restart a service sudo systemctl restart service_name
Reload config sudo systemctl reload service_name
Check status systemctl status service_name
Enable at boot sudo systemctl enable service_name
Disable at boot sudo systemctl disable service_name
Enable and start sudo systemctl enable --now service_name
Disable and stop sudo systemctl disable --now service_name
Mask (prevent starting) sudo systemctl mask service_name
Unmask sudo systemctl unmask service_name
List all services systemctl list-units --type=service
List failed services systemctl --failed
View service logs journalctl -u service_name
Follow service logs journalctl -u service_name -f
Reload all unit files sudo systemctl daemon-reload
Check boot time systemd-analyze
Check boot blame systemd-analyze blame
Shutdown sudo systemctl poweroff
Reboot sudo systemctl reboot
Default target systemctl get-default
Set default target sudo systemctl set-default target_name

Conclusion: systemd as the Foundation of Modern Linux

systemd is simultaneously the most fundamental and the most comprehensive software layer on a modern Linux system. From the moment the kernel starts PID 1, systemd orchestrates everything: starting services in parallel for fast boot, managing dependencies so nothing starts before its requirements are ready, monitoring and restarting failed services, collecting all system logs in one queryable place, and managing system state transitions from boot to shutdown.

Understanding systemd at the practical level — using systemctl to manage services, reading journalctl for logs, understanding what enabled/disabled/active/failed mean, and knowing how to create or modify unit files — gives you direct control over how your Linux system runs. This knowledge is equally valuable on a personal desktop (where you might add a custom service or debug why something is slow to start) and on a production server (where reliable service management and comprehensive logging are essential).

The three commands that cover 90% of systemd interaction: systemctl status to understand what is happening, systemctl restart to apply fixes, and journalctl -u servicename -f to watch what a service is doing in real time. Master these and you have the foundation for everything else systemd offers.

Hot this week

C++17 Structured Bindings: Unpacking Data

Master C++17 structured bindings — learn how to unpack tuples, pairs, arrays, and structs into named variables, use them in range-for loops, and extend them to custom types.

constexpr Functions: Compile-Time Computation

Master C++ constexpr functions — learn compile-time computation, consteval, constinit, compile-time containers, and how to move work from runtime to compile time for zero-cost abstractions.

Type Traits in C++: Compile-Time Type Information

Master C++ type traits — learn how to query and transform types at compile time using , write your own type traits, and use them with if constexpr and SFINAE.

What Is the Difference Between sh, bash, and zsh Shells?

Learn the difference between sh, bash, and zsh shells in Linux — their history, compatibility, features, and how to choose the right shell for scripting and daily use.

How to View File Contents in Linux Without Opening an Editor

Learn how to view file contents in Linux using cat, less, more, head, tail, and other commands — without opening a text editor. Perfect for quick checks and log monitoring.

Topics

C++17 Structured Bindings: Unpacking Data

Master C++17 structured bindings — learn how to unpack tuples, pairs, arrays, and structs into named variables, use them in range-for loops, and extend them to custom types.

constexpr Functions: Compile-Time Computation

Master C++ constexpr functions — learn compile-time computation, consteval, constinit, compile-time containers, and how to move work from runtime to compile time for zero-cost abstractions.

Type Traits in C++: Compile-Time Type Information

Master C++ type traits — learn how to query and transform types at compile time using , write your own type traits, and use them with if constexpr and SFINAE.

What Is the Difference Between sh, bash, and zsh Shells?

Learn the difference between sh, bash, and zsh shells in Linux — their history, compatibility, features, and how to choose the right shell for scripting and daily use.

How to View File Contents in Linux Without Opening an Editor

Learn how to view file contents in Linux using cat, less, more, head, tail, and other commands — without opening a text editor. Perfect for quick checks and log monitoring.

Understanding Pipes in Linux: Chaining Commands Together

Learn how Linux pipes work, how to chain commands together with the | operator, practical pipe examples, and the difference between pipes and redirection.

How to Redirect Output in Linux Commands

Learn how to redirect stdout, stderr, and stdin in Linux using >, >>, <, 2>, and 2>&1. Master output redirection with practical examples for scripts and everyday commands.

What Is the Linux Shell Profile and Bashrc File?

Learn the difference between .bashrc, .bash_profile, .profile, and /etc/profile in Linux — which files run when, what to put in each, and how to customize your shell startup.

Related Articles

Popular Categories