Modern Ubuntu Linux distributions rely on systemd as the core system and service manager, replacing legacy SysVinit and Upstart init scripts. At the centre of systemd administration is the systemctl binary, which controls service life cycles, inspects operational dependencies, manages runlevel boot targets, and orchestrates system state transitions. Mastering systemctl commands and understanding how to construct robust, resilient unit files ensures that daemonized enterprise services, web stacks, and custom microservices automatically initialise during boot, recover from crashes, and integrate smoothly with the centralised logging subsystem.
Understanding systemd Unit Types and Hierarchy
In systemd, entities managed by the system are encapsulated as “units”. Each unit corresponds to a specific configuration file with a defined extension. The table below outlines the primary unit types encountered in Ubuntu administration:
| Unit Type Extension | Functional Purpose | Administrative Example & Role |
|---|---|---|
.service |
Manages continuous background daemons and applications. | nginx.service, docker.service; controls start/stop/reload routines. |
.target |
Groups multiple units to establish specific system states (boot targets). | multi-user.target (headless console), graphical.target (desktop GUI). |
.timer |
Schedules recurring tasks; modern alternative to cron. |
apt-daily.timer; triggers associated .service units on time intervals. |
.socket |
Encapsulates IPC or network sockets for on-demand service activation. | systemd-journald.socket; starts services upon receiving socket connections. |
.mount / .automount |
Controls filesystem mount points and automated mounting. | Encapsulates /etc/fstab entries into native systemd dependency trees. |
Core Service Management Commands
To control active services during maintenance or deployment cycles, execute systemctl paired with explicit state verbs:
1. Life Cycle Control
- Starting a Service:
sudo systemctl start nginx - Stopping a Service:
sudo systemctl stop nginx - Restarting a Service:
sudo systemctl restart nginx(completely terminates the process and relaunches it). - Reloading Configuration Without Dropping Connections:
sudo systemctl reload nginx(instructs the daemon to re-read configuration files without terminating active worker threads).
2. Boot Persistence and Activation States
Starting a service only runs it in the active session. To configure persistent behaviour across server reboots:
# Enable service to start automatically at boot
sudo systemctl enable nginx
# Disable automatic boot launch
sudo systemctl disable nginx
# Enable and start concurrently in a single operation
sudo systemctl enable --now nginx
3. Service Status and Inspection
To examine real-time process diagnostics, memory consumption, active process IDs (PIDs), and the most recent log outputs:
systemctl status nginx.service
To determine if a service is running without parsing the full status block, use exit-code-friendly queries suitable for shell automation scripts:
# Returns 'active' or 'inactive'
systemctl is-active nginx
# Returns 'enabled' or 'disabled'
systemctl is-enabled nginx
# Returns 'failed' if service entered an error state
systemctl is-failed nginx
Masking Services to Prevent Accidental Execution
When deprecating a service or isolating a compromised application, simply disabling it is insufficient because other dependent services or package manager triggers can still spawn it. “Masking” links the unit file directly to /dev/null, making manual or automated activation physically impossible:
# Permanently mask the service
sudo systemctl mask apache2.service
# Restore service availability
sudo systemctl unmask apache2.service
Authoring a Custom systemd Service Unit
To run custom Node.js, Python, or Go microservices as resilient background daemons, create a dedicated unit file under /etc/systemd/system/. Files located in this directory supersede vendor configurations stored in /lib/systemd/system/.
Step 1: Create the Unit Configuration
Create a file named /etc/systemd/system/api-worker.service using your preferred text editor:
[Unit]
Description=Enterprise API Worker Service
After=network.target postgresql.service
Wants=postgresql.service
[Service]
Type=simple
User=appuser
Group=appuser
WorkingDirectory=/opt/api-worker
ExecStart=/usr/bin/python3 /opt/api-worker/main.py
Restart=always
RestartSec=5s
Environment="PORT=8080"
Environment="NODE_ENV=production"
StandardOutput=journal
StandardError=journal
LimitNOFILE=65535
[Install]
WantedBy=multi-user.target
Section analysis:
[Unit]: Declares service descriptions and sequencing.After=network.targetensures the network stack is operational before launching the service.[Service]: Defines operational execution parameters. SettingUser=appuserdrops root privileges for security.Restart=alwaysensures automatic process recovery if an unhandled exception causes a crash.[Install]: Specifies target dependencies.WantedBy=multi-user.targetensures the service starts when Ubuntu enters standard multi-user console/server mode.
Step 2: Reload the Daemon and Enable the Unit
Whenever you create or modify a unit file, you must notify systemd to re-read all disk configurations before starting the service:
# Re-scan all unit configuration directories
sudo systemctl daemon-reload
# Enable and initiate the newly configured service
sudo systemctl enable --now api-worker.service
Managing Boot Targets and System Runlevels
In systemd, traditional SysVinit runlevels (0–6) are represented by .target units. Managing targets allows administrators to transition between maintenance modes, text-only consoles, and graphical desktop interfaces.
# View current default boot target
systemctl get-default
# Set default boot target to headless multi-user mode (ideal for servers)
sudo systemctl set-default multi-user.target
# Set default boot target to graphical interface
sudo systemctl set-default graphical.target
# Switch active system immediately into emergency maintenance mode
sudo systemctl isolate rescue.target
Inspecting Unit Logs with journalctl
All standard output (stdout) and standard error (stderr) generated by a systemd unit are automatically routed into the binary system journal. To inspect logs cleanly without traversing disparate log directories:
# Tail live logs for a specific unit in real time
journalctl -u api-worker.service -f
# View logs emitted since the most recent system reboot
journalctl -u api-worker.service -b
# Filter logs for errors only (priority level 3)
journalctl -u api-worker.service -p err..emerg
Troubleshooting Failed systemd Services
When a unit fails to start or continuously crashes upon initialisation, apply the following diagnostic methodology:
- Locating All Failed Units: Execute
systemctl --failedto generate an immediate list of degraded services across the entire operating system. - Examining Exact Failure Reasons: Inspect
systemctl status <unit>and look at theProcess: ... (code=exited, status=...)line. Status1usually denotes an application runtime crash, status127indicates a missing executable path, and status203/EXECsignifies permission denied on the binary. - Resetting Failed State: After modifying scripts or addressing configuration syntax issues, clear the unit’s failed flag from the systemd status tree using:
sudo systemctl reset-failed api-worker.service - Validating Working Directories and Permissions: If
User=is specified in the service file, verify that the designated user account owns theWorkingDirectoryand has execution rights over the launch script.