How to Manage Background Services in Ubuntu Using systemd

Every modern Linux distribution depends on background tasks to deliver core functionality, run web applications, handle incoming network traffic, and maintain system stability. In Ubuntu, these background processes are known as daemons or system units. Whether you run a local development machine or manage production cloud instances, having precise control over Ubuntu services is one of the most critical skills you can develop as an administrator.

Since the release of Ubuntu 15.04, the standard service manager across the operating system has been systemd. Replacing older initialization architectures like Upstart and SysVinit, systemd provides an integrated suite of tools to initialize the user space, orchestrate parallel startup tasks, monitor running processes, and track application output. In this detailed guide, we will explore everything you need to know about supervising, troubleshooting, configuring, and building background services on Ubuntu.

Understanding systemd and Background Daemons in Ubuntu

Before executing commands, it helps to understand what a service actually is inside an operating system. A service—traditionally called a daemon—is a program that executes in the background without direct user interaction through a graphical interface or open shell terminal. Examples include web servers like Nginx or Apache, database engines like PostgreSQL, remote access utilities like OpenSSH, and time synchronization daemons like systemd-timesyncd.

Under the systemd architecture, background services are represented by configuration profiles called unit files. These unit files end with the .service extension and explain how the system should handle the lifecycle of an application: how to start it, what dependencies must be running first, how to handle unexpected crashes, and what privileges the application should hold.

The primary utility used to interact with these unit files is systemctl. Through systemctl, you can query status, alter runtime execution, adjust boot configuration, and inspect dependencies across all registered Ubuntu services.

Listing and Inspecting Ubuntu Services

When starting an administrative session, your first priority is often to evaluate what is currently active on the machine. Running systemctl without arguments outputs an exhaustive list of loaded systemd units, which can quickly overwhelm your terminal. Fortunately, systemd provides several flags to narrow your search.

Listing Active Services

To view only units explicitly categorized as services that are currently active in memory, run the following command:

systemctl list-units --type=service

This command outputs a table containing several columns:

  • UNIT: The identifier of the service (for example, ssh.service or cron.service).
  • LOAD: Indicates whether systemd successfully parsed the service configuration file into memory.
  • ACTIVE: The high-level state of the service, commonly marked as active, reloading, or inactive.
  • SUB: The low-level operational state, such as running, exited, or waiting.
  • DESCRIPTION: A human-readable summary of the service’s responsibility.

Listing All Installed Services Regardless of State

If a service is stopped or disabled, it will not appear in the active units listing. To view every service configuration discovered on your filesystem, execute:

systemctl list-unit-files --type=service

This output highlights the unit file alongside its persistent boot state, indicating whether the service is configured to launch during the system boot sequence (enabled), ignored during boot (disabled), or strictly prevented from running (masked).

Managing the Service Lifecycle: Start, Stop, and Restart

Controlling running Ubuntu services requires standard administrative privileges. You will typically prepend sudo to modifications that alter the system’s operational state.

Starting an Inactive Service

To launch an inactive daemon immediately during the current session, use the start subcommand:

sudo systemctl start nginx.service

Note that appending the .service suffix is optional when specifying unit names. Running sudo systemctl start nginx executes the exact same operation.

Stopping an Active Service

When performing maintenance, upgrading application binaries, or freeing up system resources, you can halt a running daemon using the stop command:

sudo systemctl stop nginx.service

This dispatches a termination signal (usually SIGTERM) to the process, allowing it to close open connections, flush buffers, and terminate cleanly. If the process hangs, systemd will eventually issue a SIGKILL to release system resources.

Restarting vs. Reloading

Modifying configuration files often necessitates applying changes to the running daemon. System administrators have two distinct strategies for applying changes:

  • Restart: Completely terminates the service process and immediately initiates it again. This momentarily drops existing client connections:
    sudo systemctl restart nginx.service
  • Reload: Instructs the running process to re-parse its configuration files without dropping active connections or terminating the process tree:
    sudo systemctl reload nginx.service

Not every daemon supports dynamic configuration reloading. If you are unsure whether a target service supports reload functionality, systemd provides an intelligent fallback command:

sudo systemctl reload-or-restart nginx.service

Checking Detailed Service Status

To confirm that a service is functioning properly, diagnose unexpected crashes, or check uptime, use the status command:

systemctl status ssh.service

The resulting output contains vital diagnostic metrics:

  • Loaded: Path to the unit file on disk and whether it is enabled at boot.
  • Active: Precise runtime duration, uptime timestamp, and active status.
  • Docs: Associated manual pages or documentation links.
  • Main PID: The primary Linux process identifier.
  • Tasks: The current count of running threads and the configured resource limits.
  • Memory: Real-time RAM consumption allocated to the unit’s control group (cgroup).
  • CPU: Aggregated CPU time consumed by the service since launch.
  • Log snippet: The most recent journal log entries emitted by the service.

For automated deployment scripts or continuous integration routines, human-readable status outputs can be cumbersome to parse. Instead, use systemd query flags that return predictable text and zero or non-zero exit codes:

systemctl is-active ssh
systemctl is-failed ssh

Configuring Boot Behavior: Enabling and Disabling Services

A common misconception among beginner Linux users is that starting a service guarantees it will run after the next system reboot. However, in systemd, runtime state is separate from boot behavior.

Enabling Services on Boot

To configure an application to start automatically when Ubuntu boots, run the enable command:

sudo systemctl enable docker.service

Under the hood, systemd does not modify the application binaries. Instead, it creates a symbolic link (symlink) from the unit file located in /lib/systemd/system/ or /etc/systemd/system/ into the target dependency folder (such as /etc/systemd/system/multi-user.target.wants/). When the multi-user boot milestone is reached, all linked units launch.

If you want to start the service immediately and register it for boot initialization in a single command, use the --now flag:

sudo systemctl enable --now docker.service

Disabling Services on Boot

To stop a service from automatically starting upon boot without altering its current runtime state, run:

sudo systemctl disable docker.service

This operation simply removes the previously created symbolic links. To stop the running daemon and simultaneously revoke its boot persistence, run:

sudo systemctl disable --now docker.service

Masking Problematic Services

In complex environments, a disabled service might still be triggered indirectly if another active service explicitly lists it as a dependency. If you must guarantee that a service cannot run under any circumstances, use the mask command:

sudo systemctl mask apache2.service

Masking links the unit file directly to /dev/null. Any attempt—manual or automated—to start the service will be rejected immediately with an error. To restore normal operation later, run:

sudo systemctl unmask apache2.service

Inspecting Service Logs with journalctl

When an Ubuntu service fails to initialize or begins throwing errors, viewing raw terminal output is often insufficient. systemd integrates process management with a unified logging framework called journald. Instead of searching through fragmented log files in /var/log, you can inspect service logs directly using journalctl.

Viewing Logs for a Specific Service

Filter log output directly by the target unit name using the -u flag:

journalctl -u nginx.service

Following Live Log Streams

To monitor application output in real time—similar to the traditional tail -f command—add the -f flag:

journalctl -u nginx.service -f

Limiting Output by Timeframe

When diagnosing incidents, you often need to isolate events to a specific incident window. You can filter logs by time or boot session:

journalctl -u nginx.service --since "1 hour ago"
journalctl -u nginx.service --since "2024-01-01 00:00:00" --until "2024-01-01 02:00:00"
journalctl -u nginx.service -b

The -b flag isolates logs recorded strictly during the current operating system boot cycle, discarding historical data from prior system runs.

Creating a Custom Background Service in Ubuntu

Managing standard packages is useful, but system administrators often need to daemonize internal scripts, Node.js applications, Python automation tools, or Go microservices. Creating a custom service under systemd is simple and robust.

Step 1: Place Your Executable

Assume you have a custom monitoring script located at /usr/local/bin/metrics-collector.sh. Ensure the script has proper execution permissions:

sudo chmod +x /usr/local/bin/metrics-collector.sh

Step 2: Create the Unit Configuration

User-defined unit files should always be placed inside /etc/systemd/system/, as configurations in this directory override vendor defaults. Create a new file:

sudo nano /etc/systemd/system/metrics-collector.service

Populate the file with the following configuration block:

[Unit]
Description=Internal Metrics Collection Daemon
After=network.target

[Service]
Type=simple
User=www-data
Group=www-data
WorkingDirectory=/usr/local/bin
ExecStart=/usr/local/bin/metrics-collector.sh
Restart=on-failure
RestartSec=5s

[Install]
WantedBy=multi-user.target

Here is what these configuration directives accomplish:

  • [Unit]: Contains metadata. The After=network.target directive ensures systemd delays running this service until network capabilities are fully initialized.
  • [Service]: Defines operational parameters. We set Type=simple, drop root privileges by specifying User=www-data, identify the execution command via ExecStart, and instruct systemd to automatically restart the service if it exits with an error code.
  • [Install]: Declares where this service hooks into the boot tree. WantedBy=multi-user.target ensures the unit loads during standard, non-graphical multi-user boot states.

Step 3: Reload the Daemon and Start

Whenever you add or modify a unit file inside /etc/systemd/system/, you must notify systemd to re-read the configuration directory:

sudo systemctl daemon-reload

Leave a Comment