systemd Explained: Managing Services on Linux

systemd Explained: Managing Services on Linux

systemd is the init system used by virtually all major Linux distributions today — Ubuntu, Debian, RHEL, Fedora, Rocky Linux, and more. It manages the boot process, handles services, mounts filesystems, and manages logging. Understanding systemd is essential for any modern Linux sysadmin.

What Is systemd?

systemd is PID 1 — the first process the kernel starts. It then starts everything else in parallel, which makes modern Linux boot much faster than older init systems. It organizes system resources as units: services, sockets, timers, mounts, and more.

systemctl --version
ps -p 1           # Confirm PID 1 is systemd

Managing Services with systemctl

systemctl status nginx            # Show service status, recent logs
systemctl start nginx             # Start immediately
systemctl stop nginx              # Stop immediately
systemctl restart nginx           # Stop + start
systemctl reload nginx            # Reload config (no downtime if supported)
systemctl enable nginx            # Start at boot
systemctl disable nginx           # Do not start at boot
systemctl enable --now nginx      # Enable AND start immediately
systemctl is-active nginx         # Prints "active" or "inactive" (exit code 0/1)
systemctl is-enabled nginx        # Prints "enabled" or "disabled"

Listing Units

systemctl list-units                          # All active units
systemctl list-units --type=service           # Services only
systemctl list-units --state=failed           # Failed units
systemctl list-unit-files                     # All installed unit files
systemctl list-unit-files --type=service | grep enabled   # Enabled services

Viewing Logs with journalctl

systemd captures all service output in the journal:

journalctl                        # All logs (newest at bottom)
journalctl -b                     # Logs from current boot only
journalctl -b -1                  # Logs from previous boot
journalctl -u nginx               # Logs for nginx service
journalctl -u nginx -n 50         # Last 50 lines for nginx
journalctl -u nginx -f            # Follow (live tail)
journalctl --since "2026-04-01"   # Logs since date
journalctl --since "1 hour ago"   # Last hour
journalctl -p err                 # Only errors
journalctl -k                     # Kernel messages only

Writing a systemd Service Unit

Unit files live in /etc/systemd/system/. Here is a complete example for a Node.js application:

sudo nano /etc/systemd/system/myapp.service
[Unit]
Description=My Node.js Application
After=network.target
Wants=network-online.target

[Service]
Type=simple
User=www-data
Group=www-data
WorkingDirectory=/opt/myapp
ExecStart=/usr/bin/node /opt/myapp/server.js
Restart=on-failure
RestartSec=5
StandardOutput=journal
StandardError=journal
Environment=NODE_ENV=production PORT=3000

[Install]
WantedBy=multi-user.target
sudo systemctl daemon-reload          # Reload unit files after editing
sudo systemctl enable --now myapp     # Enable and start
sudo systemctl status myapp           # Verify

Unit File Directives Explained

  • [Unit] — metadata and ordering. After= means start after these units are up.
  • [Service] — the actual service definition.
  • Type=simple — process stays in foreground (most common).
  • Restart=on-failure — auto-restart if the process exits with error.
  • RestartSec=5 — wait 5 seconds before restarting.
  • [Install] — defines when to enable. multi-user.target = normal multi-user mode.

Timers (Replacing Cron)

systemd timers are a modern alternative to cron, with better logging and dependency handling:

# /etc/systemd/system/backup.timer
[Unit]
Description=Run backup daily

[Timer]
OnCalendar=daily
Persistent=true

[Install]
WantedBy=timers.target
sudo systemctl enable --now backup.timer
systemctl list-timers                     # List all active timers

Analyzing Boot Performance

systemd-analyze                          # Total boot time
systemd-analyze blame                    # Time per unit
systemd-analyze critical-chain           # Bottleneck chain
systemd-analyze plot > boot.svg          # Visual boot chart

Target Units (Replacing Runlevels)

systemctl get-default                    # Default target (usually graphical or multi-user)
sudo systemctl set-default multi-user.target   # Boot to text mode
sudo systemctl isolate rescue.target     # Switch to rescue mode

Dependency Management

systemctl list-dependencies nginx        # What nginx depends on
systemctl list-dependencies --reverse nginx   # What depends on nginx

Summary

systemd is the backbone of modern Linux systems. Use systemctl to manage services, journalctl to read logs, and unit files to define your own services. Writing a clean service unit with Restart=on-failure and proper logging ensures your applications are resilient and debuggable in production.

Frequently Asked Questions

  • What is the difference between systemctl restart and systemctl reload?
    systemctl restart servicename stops the service completely and then starts it fresh — all in-flight connections or sessions are dropped. systemctl reload servicename sends a SIGHUP signal to the running process, instructing it to re-read its configuration without stopping. Reload causes zero downtime when the service supports it (Nginx, Apache, and PostgreSQL all handle SIGHUP gracefully). If a service does not implement in-place reload, reload will fail or do nothing — check the unit file for a ExecReload= directive to confirm support. Use reload for configuration changes in production; use restart when you need to apply binary updates or the service is in a bad state.
  • Why does my service start but immediately stop?
    The most common cause is an error in the startup command or the service configuration file. Check the path in ExecStart= is correct and the binary is executable. Then read the logs — this is almost always where the answer is:
    journalctl -u your-service-name -n 50 --no-pager
    Look for lines marked FAILED or any error message from the application itself. Also verify that any EnvironmentFile= or configuration files referenced by the service actually exist and are readable by the service's user.
  • How do I know if a service is set to start at boot?
    Use systemctl is-enabled servicename. It returns enabled if the service will start at boot, disabled if it will not, or static if it is controlled by another unit. To enable a service for boot: sudo systemctl enable servicename. To enable and start it immediately in one command: sudo systemctl enable --now servicename. Note that systemctl start starts the service now but does not make it persistent across reboots — you need enable for that.
  • What is the difference between Type=simple and Type=forking?
    Type=simple (the default) tells systemd that the process listed in ExecStart= is the main process of the service and stays in the foreground. systemd tracks it directly. Type=forking is for traditional Unix daemons that fork a child process and then have the parent exit. systemd waits for the parent to exit and then tracks the child. Setting the wrong type causes problems: if a forking daemon is declared as simple, systemd thinks the service died when the parent exits and may restart it. When in doubt, check the daemon's documentation or look at its official upstream systemd unit file.
  • How do I run a systemd service as a specific user?
    In the [Service] section of the unit file, add:
    [Service]
    User=myappuser
    Group=myappgroup
    systemd will drop privileges and run the service as that user before executing ExecStart=. The user must exist on the system. For security, create a dedicated system user with no login shell: sudo useradd -r -s /usr/sbin/nologin myappuser. You can also combine this with WorkingDirectory= to set the working directory and EnvironmentFile= to load secrets from a file owned and readable only by that user.