What Is systemd?
When the Linux kernel finishes booting, it hands off control to a single process: PID 1. On almost every major Linux distribution in use today, that process is systemd. Everything that happens after that — starting services, mounting filesystems, managing users sessions, handling device hotplug — runs under systemd’s supervision.
Understanding systemd explains a lot of what was previously opaque: why service nginx start works differently from systemctl start nginx, what that /lib/systemd/system/ directory contains, and why log files sometimes live in /var/log/ and sometimes in the journal.
What systemd replaced
Before systemd, Linux used SysV init. Init started by running a single master script that called other scripts in numbered order: S01sysinit, S10network, S20nginx, and so on. The number determined the order. Scripts ran one after another, serially.
This worked but had serious limitations. Serial startup was slow. Dependencies were expressed only by ordering, not by explicit declaration. Service management (starting, stopping, restarting) happened through shell scripts with no uniform interface. Log files were scattered across /var/log/ with no standard format or central query tool.
systemd addressed all of these at once, which is why its adoption was rapid despite significant controversy among users who preferred the simplicity of shell scripts.
The unit: systemd’s fundamental object
Everything systemd manages is a unit. A unit is a configuration object described by a plain text file. The unit type is determined by the file extension:
| Extension | Type | Purpose |
|---|---|---|
.service | Service | Start, stop, and monitor a daemon or one-shot process |
.socket | Socket | Activate a service when traffic arrives on a socket |
.timer | Timer | Run a service on a schedule |
.mount | Mount | Mount a filesystem |
.automount | Automount | Mount a filesystem on demand |
.target | Target | Group units together, define system states |
.path | Path | Activate a service when a file or directory changes |
.scope | Scope | Manage groups of externally created processes |
.slice | Slice | Manage a cgroup hierarchy for resource control |
.device | Device | React to hardware device events |
Unit file locations
# Package-installed unit files (do not edit these directly)
/lib/systemd/system/
/usr/lib/systemd/system/
# Administrator overrides (edit these)
/etc/systemd/system/
# User-level units
~/.config/systemd/user/
# List where systemd looks for unit files
systemctl show -p UnitPath systemd
# Override a package's unit file without replacing it
# (survives package updates)
sudo systemctl edit nginx.service # creates a drop-in file
Anatomy of a service unit file
# /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
Wants=network-online.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
KillMode=mixed
KillSignal=SIGQUIT
TimeoutStopSec=5s
PrivateTmp=true
[Install]
WantedBy=multi-user.target
The three sections:
[Unit] contains metadata and dependency declarations:
Description: human-readable name shown bysystemctl statusAfter: this unit starts after these units are active (ordering only, not dependency)Requires: this unit requires these units to be activeWants: like Requires but does not fail if the dependency fails (weaker)
[Service] describes how to manage the process:
Type: how systemd knows the service is ready (simple,forking,notify,oneshot,dbus,idle)ExecStart: the command to start the serviceExecStop: the command to stop it (optional; systemd sends SIGTERM if not specified)ExecReload: the command to reload configuration (optional)Restart: when to restart (on-failure,always,on-abnormal,no)RestartSec: how long to wait before restartingUser/Group: which user/group to run as (instead of root)
[Install] determines when the unit is enabled:
WantedBy=multi-user.targetmeanssystemctl enablecreates a symlink that makes this service start when multi-user.target is reached (normal boot)
Service types
The Type= field in a service unit tells systemd how to know when the service has finished starting:
# simple (default): ExecStart is the main process; systemd considers it started immediately
Type=simple
# forking: ExecStart forks a child; parent exits; child is the daemon
# systemd waits for the parent to exit, then considers it started
Type=forking
PIDFile=/run/myapp.pid
# notify: the service sends a sd_notify() message when ready
# More reliable than forking; used by modern daemons
Type=notify
# oneshot: ExecStart runs and exits; systemd waits for it to finish
# Used for setup scripts that are not long-running
Type=oneshot
RemainAfterExit=yes
# dbus: service is considered ready when it acquires a D-Bus name
Type=dbus
BusName=org.example.Service
Targets: replacing runlevels
Targets group units into system states. The traditional SysV runlevels map to systemd targets:
| SysV Runlevel | systemd Target |
|---|---|
| 0 | poweroff.target |
| 1 | rescue.target |
| 3 | multi-user.target |
| 5 | graphical.target |
| 6 | reboot.target |
# See the current default target
systemctl get-default
# Change the default target
sudo systemctl set-default multi-user.target
sudo systemctl set-default graphical.target
# Switch to a target right now (like changing runlevel)
sudo systemctl isolate rescue.target
# See what units are in a target
systemctl list-dependencies multi-user.target
# See all targets
systemctl list-units --type=target
The cgroup connection
Every service unit runs its processes inside a cgroup (control group). This has an important implication: systemd always knows every process that belongs to a service, even if the service forks child processes. When you stop a service, systemd kills every process in its cgroup, not just the main process. No orphaned processes escape.
# See the cgroup tree
systemd-cgls
# See which service a process belongs to
systemctl status $(pgrep nginx | head -1)
# See resource usage by service
systemd-cgtop
# Set CPU and memory limits on a service
sudo systemctl set-property nginx.service CPUQuota=50%
sudo systemctl set-property nginx.service MemoryMax=512M
The journal
journald collects log output from all services and stores it in a structured binary format:
# View all logs
journalctl
# Follow logs in real time (like tail -f)
journalctl -f
# Logs from a specific service
journalctl -u nginx
journalctl -fu nginx # follow
# Logs from the current boot only
journalctl -b
# Logs from the previous boot
journalctl -b -1
# Filter by priority (0=emergency through 7=debug)
journalctl -p 3 # errors and above
journalctl -p err
# Filter by time
journalctl --since "2026-06-28 10:00:00"
journalctl --since "1 hour ago"
journalctl --since "09:00" --until "10:00"
# Output formats
journalctl -u nginx -o json-pretty # JSON with all metadata
journalctl -u nginx -o cat # message text only
# Disk usage of the journal
journalctl --disk-usage
# Limit journal size in /etc/systemd/journald.conf
# SystemMaxUse=500M
Socket activation
Socket activation is one of systemd’s more elegant features. Instead of starting a service at boot and having it listen on a port, systemd holds the socket open and starts the service only when a connection arrives:
# Example: the SSH daemon socket
cat /lib/systemd/system/ssh.socket
# List all socket units
systemctl list-units --type=socket
# Check if a socket is active
systemctl status ssh.socket
Socket activation improves boot time (services start on demand) and allows zero-downtime restarts (systemd holds the socket during restart so no connections are dropped).
Writing your own service unit
# Create a simple service unit for a custom application
sudo nano /etc/systemd/system/myapp.service
[Unit]
Description=My Application
After=network.target
[Service]
Type=simple
User=myapp
Group=myapp
WorkingDirectory=/opt/myapp
ExecStart=/opt/myapp/bin/myapp --config /etc/myapp/config.toml
Restart=on-failure
RestartSec=5s
StandardOutput=journal
StandardError=journal
SyslogIdentifier=myapp
# Security hardening
NoNewPrivileges=yes
PrivateTmp=yes
ProtectSystem=strict
ProtectHome=yes
ReadWritePaths=/var/lib/myapp /var/log/myapp
[Install]
WantedBy=multi-user.target
# After creating or modifying a unit file, reload systemd
sudo systemctl daemon-reload
# Enable and start the service
sudo systemctl enable --now myapp.service
# Check it is running
systemctl status myapp.service
# Follow its logs
journalctl -fu myapp.service
systemd’s reach extends well beyond just starting services: it manages timers, mounts, device events, and user sessions. Most interaction with it happens through systemctl and journalctl, covered in the companion articles. The unit file format is the language that ties all of it together.
Frequently Asked Questions
What is systemd?
systemd is the init system and service manager used by most major Linux distributions, including Ubuntu, Fedora, Debian, Arch, and openSUSE. It is the first process started by the kernel after boot (PID 1) and is responsible for bringing up the rest of the system: starting services, mounting filesystems, managing devices, and handling user sessions. It replaced the older SysV init and Upstart systems.
Why did Linux switch from SysV init to systemd?
SysV init started services sequentially using shell scripts, which was slow and hard to manage. Upstart added parallel startup and event-driven service management but was limited. systemd addressed several problems at once: services start in parallel with explicit dependency declarations, so boot times are faster. Services are tracked in cgroups so systemd always knows which processes belong to which service. Failed services can be restarted automatically. The journal replaces scattered log files with a structured, queryable log store. Dependencies between units are declared explicitly rather than inferred from numeric ordering.
What is a systemd unit?
A unit is the basic configuration object in systemd. Each unit has a type (service, socket, timer, mount, target, etc.) and is described by a unit file — a plain text INI-style configuration file. A service unit describes how to start, stop, and monitor a daemon. A timer unit describes when to run a service. A socket unit describes a socket that can activate a service on demand. Unit files live in /lib/systemd/system/ (installed by packages) and /etc/systemd/system/ (administrator overrides).
What is a systemd target?
A target is a systemd unit type that groups other units together, similar to SysV runlevels. The default target on a server is multi-user.target (text login, all services). On a desktop it is graphical.target (multi-user plus a display manager). Other targets include rescue.target (single-user mode for recovery), emergency.target (minimal environment), and network.target (networking is up). You switch between targets with systemctl isolate target-name or set the default with systemctl set-default.
What is the systemd journal?
The systemd journal is a structured logging system managed by journald, a component of systemd. Unlike traditional syslog, which stores logs as plain text files, the journal stores logs in a binary format with structured metadata: the unit that generated the log, the PID, the timestamp, the priority level, and more. You query the journal with journalctl. The structured format enables powerful filtering: by unit, by time, by priority, by boot, or by arbitrary metadata fields. Logs can optionally be forwarded to a traditional syslog daemon as well.