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:
- The kernel decompresses itself
- Initializes hardware subsystems (memory, CPU, buses)
- Mounts the root filesystem (initially a temporary initramfs if needed)
- 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:
- Reads its configuration — system configuration in
/etc/systemd/system/and defaults in/usr/lib/systemd/system/ - 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 bemulti-user.target. - 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. - 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.
- Monitors startup — systemd tracks which units started successfully, which failed, and which are still starting.
Stage 3: Reaching the Target
As services start:
systemd-journaldstarts early to capture logs from the beginningsystemd-udevdstarts and processes hardware events- Network configuration (
NetworkManagerorsystemd-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 outputDocumentation— 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 starteddbus— service registers on D-Bus when ready
ExecStart— the command to start the serviceExecStop— how to stop the serviceExecReload— how to reload configuration without restartingRestart— when to automatically restart:on-failure,always,on-abnormalRestartSec— seconds to wait before restartingUser/Group— run as this user/groupPrivateTmp— 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.




