Skip to content

Service Management

systemd Units and Targets covered how unit files are structured and how they depend on each other. Now it's time to put that knowledge into daily practice — starting, stopping, enabling .service units at boot, and checking their current state. This is one of the tasks a Linux administrator performs most often: a web server, a database, or your own backend application are all managed through exactly the same set of commands.

What You Will Learn

By the end of this article you will be able to:

  • explain the difference between systemctl start/stop/restart/reload;
  • configure automatic startup at boot with enable/disable;
  • read every field in systemctl status output;
  • use script-friendly checks such as is-active, is-enabled, is-failed;
  • explain why mask/unmask exists and how it differs from disable.

Starting, Stopping, and Restarting a Service

sudo systemctl start nginx
sudo systemctl stop nginx
sudo systemctl restart nginx

These three are self-explanatory: start starts a service, stop stops it, and restart stops it completely and starts it again. restart works for any service that reads its configuration on startup, but because it kills the process entirely before recreating it, the service is briefly unavailable, even if only for a moment.

sudo systemctl reload nginx

reload, in contrast, forces a service to re-read its configuration without stopping it — the standard way to apply a configuration change with zero downtime, if the service supports it. Not every service supports reload; in that case systemctl reload nginx fails outright and restart has to be used instead.

Tip

If it's unclear whether a service supports reload, systemctl reload-or-restart nginx is a safe choice — it tries reload first and falls back to restart if that isn't possible.

Automatic Startup at Boot: enable and disable

start/stop only affect the current session — after a reboot, the service returns to whatever state it was in before. Whether a service starts automatically on every boot is controlled by a separate pair of commands:

sudo systemctl enable nginx
sudo systemctl disable nginx

enable creates a symlink in the appropriate target directory (for example, /etc/systemd/system/multi-user.target.wants/), based on the WantedBy= value in the unit file's [Install] section, covered in systemd Units and Targets. disable removes that symlink.

enable and start are two independent actions, which means four combinations are possible:

State Enabled? Running right now?
Freshly installed, not yet configured No No
Started manually just to test it No Yes
Enabled for boot, but no reboot yet Yes No
Fully working, production state Yes Yes

A convenient shorthand for doing both at once:

sudo systemctl enable --now nginx

This performs enable and start in a single call — the form used most often when setting up a new service, and the same pattern used in Creating a Custom systemd Service.

Checking a Service's Status

systemctl status nginx
● nginx.service - A high performance web server and reverse proxy server
     Loaded: loaded (/lib/systemd/system/nginx.service; enabled; preset: enabled)
     Active: active (running) since Fri 2026-07-24 09:02:11 UTC; 1h 20min ago
       Docs: man:nginx(8)
   Main PID: 1842 (nginx)
      Tasks: 3 (limit: 4665)
     Memory: 4.2M
        CPU: 128ms
     CGroup: /system.slice/nginx.service
             ├─1842 "nginx: master process /usr/sbin/nginx"
             └─1843 "nginx: worker process"

The important fields:

  • Loaded — whether the unit file was found, and its enabled/disabled state;
  • Active — the primary state (active, inactive, failed) plus a more detailed state in parentheses (running, exited, dead);
  • Main PID — the service's primary process ID;
  • CGroup — every process belonging to the service, the systemd-level view of the process tree covered in /proc Filesystem.

systemctl status also shows the last few log lines, but the full history is read with the dedicated tool covered in journalctl.

Script-Friendly Status Checks

Instead of the interactive status, when an automated script only needs an exit code, the following are more convenient:

systemctl is-active nginx
echo $?
active
0
Command What it returns
systemctl is-active <unit> active/inactive/failed, with a matching exit code
systemctl is-enabled <unit> enabled/disabled, with a matching exit code
systemctl is-failed <unit> Returns 0 if the unit is in failed state, 1 otherwise

These commands are the basic building block of health-check and monitoring scripts, the kind covered in the Bash Scripting Basics module — the exit code makes conditionals like if systemctl is-active --quiet nginx; then ... fi possible.

mask and unmask: Stronger than disable

sudo systemctl mask apache2

disable only turns off automatic startup — the service can still be started manually with systemctl start. mask, on the other hand, symlinks the unit file to /dev/null, so it cannot be started at all, whether manually or as a dependency of another unit:

sudo systemctl start apache2
Failed to start apache2.service: Unit apache2.service is masked.

Removing the block:

sudo systemctl unmask apache2

mask is typically used when two services genuinely conflict (for example, apache2 and nginx both trying to bind the same port on one server), or when a security policy requires that a specific service be blocked entirely.

Common Mistakes

Enabling a Service but Forgetting to Start It

The service is enabled, but not running yet — because enable only affects the next boot. To start it immediately, use enable --now or a separate start.

Confusing restart and reload

Using restart on a high-traffic production service for a routine configuration change causes a brief outage, even if the service actually supports reload. Prefer reload or reload-or-restart whenever possible.

Confusing disable with mask

A disable-d service can still be started as a dependency by another unit (for example, via Requires=). If a service must never start under any circumstances, use mask.

Trying to enable a Unit With No [Install] Section

sudo systemctl enable mytimer.service
The unit files have no installation config (WantedBy=, RequiredBy=, Also=, Alias=
settings in the [Install] section, and DefaultInstance= for template units).

enable works by creating the symlink described by WantedBy=/RequiredBy= in the unit's [Install] section — a unit that has no such section simply cannot be enabled, and the command fails with the message above rather than silently doing nothing. The fix is to add an [Install] section (see Creating a Custom systemd Service). A unit that is only ever pulled in as a dependency of another unit legitimately has no [Install] section and is not meant to be enabled directly.

Exercises

  1. In a test VM, run an existing service (for example, cron) through stop, status, start and observe how the Active field changes at each step.
  2. disable the same service, reboot (or simulate it with systemctl daemon-reload), and explain how the is-enabled/is-active results differ.
  3. mask a harmless service, try to start it and read the error message, then reverse it with unmask.
  4. Write a conditional like systemctl is-active --quiet nginx && echo "running" || echo "not running" and observe how the exit code of is-active is used in practice.

Verification criterion: you should be able to state, for a service in each of the four enable/running combinations from the table above, exactly which command moves it into a different combination.

Summary

start/stop/restart/reload control a service's current state, while enable/disable control its automatic startup at boot — two independent dimensions, often combined with enable --now. systemctl status gives a detailed view, while is-active/is-enabled/is-failed give short, exit-code-friendly checks for scripts. mask, unlike disable, blocks a service from starting through any path at all. The next article, Creating a Custom systemd Service, applies these same commands not to an existing service, but to a new .service file you write yourself.

References