ZetCode

Linux systemctl Command

last modified October 23, 2026

The systemctl command controls systemd, the init system and service manager of most current Linux distributions. It starts, stops, and restarts services, enables them at boot, shows their status, and manages other kinds of units such as timers, sockets, mounts, and targets.

The basic syntax is systemctl [options] command [unit...]. Commands that change the system need root privileges, so they are run with sudo; reading the state works for regular users. The examples were run on Debian 12 with systemd 252, on a machine called webserver that runs nginx, cron, and an SSH server. The .service suffix can be omitted: nginx means nginx.service.

Checking the system state

The is-system-running command reports the overall state of the service manager.

systemctl is-system-running

running means that the system has fully started and no unit has failed. Other common values are starting during boot and degraded when at least one unit has failed; an example follows later.

$ systemctl is-system-running
running

Listing running services

The list-units command lists the units that systemd has in memory. The --type and --state options filter the list.

systemctl list-units --type=service --state=running

LOAD tells whether the unit file was read successfully, ACTIVE is the general state (active, inactive, failed), and SUB the detailed one, such as running or exited. In a terminal, systemctl pipes long output through a pager; the --no-pager option turns that off.

$ systemctl list-units --type=service --state=running
  UNIT                     LOAD   ACTIVE SUB     DESCRIPTION
  cron.service             loaded active running Regular background program processing daemon
  dbus.service             loaded active running D-Bus System Message Bus
  nginx.service            loaded active running A high performance web server and a reverse proxy server
  ssh.service              loaded active running OpenBSD Secure Shell server
  systemd-journald.service loaded active running Journal Service
  systemd-logind.service   loaded active running User Login Management

LOAD   = Reflects whether the unit definition was properly loaded.
ACTIVE = The high-level unit activation state, i.e. generalization of SUB.
SUB    = The low-level unit activation state, values depend on unit type.
6 loaded units listed.

Status of a service

The status command is the first thing to check when a service misbehaves.

systemctl status nginx

The Loaded line shows the unit file, whether the service is enabled at boot, and the vendor preset. Active shows the state and how long it has lasted. Below are the main PID, the number of tasks, memory and CPU usage, and the process tree of the service's control group. The last lines come from the journal and often explain why a service failed. The dot before the name is green for an active unit.

$ systemctl status nginx
● nginx.service - A high performance web server and a reverse proxy server
     Loaded: loaded (/lib/systemd/system/nginx.service; enabled; preset: enabled)
     Active: active (running) since Fri 2026-10-23 13:37:49 CEST; 3s ago
       Docs: man:nginx(8)
    Process: 78 ExecStartPre=/usr/sbin/nginx -t -q -g daemon on; master_process on; (code=exited, status=0/SUCCESS)
    Process: 79 ExecStart=/usr/sbin/nginx -g daemon on; master_process on; (code=exited, status=0/SUCCESS)
   Main PID: 80 (nginx)
      Tasks: 3 (limit: 2811)
     Memory: 2.2M
        CPU: 19ms
     CGroup: /system.slice/nginx.service
             ├─80 "nginx: master process /usr/sbin/nginx -g daemon on; master_process on;"
             ├─81 "nginx: worker process"
             └─82 "nginx: worker process"

Oct 23 13:37:49 webserver systemd[1]: Starting nginx.service - A high performance web server and a reverse proxy server...
Oct 23 13:37:49 webserver systemd[1]: Started nginx.service - A high performance web server and a reverse proxy server.

Starting, stopping, and restarting

The start, stop, restart, and reload commands change the current state of a service.

sudo systemctl stop nginx
systemctl is-active nginx
sudo systemctl start nginx
sudo systemctl reload nginx
sudo systemctl restart nginx
systemctl is-active nginx

These commands print nothing on success. reload asks the service to reread its configuration without stopping; nginx keeps serving existing connections. Not every service supports it, and reload-or-restart falls back to a restart when it does not. restart stops and starts the service, which briefly interrupts it.

$ sudo systemctl stop nginx
$ systemctl is-active nginx
inactive
$ sudo systemctl start nginx
$ sudo systemctl reload nginx
$ sudo systemctl restart nginx
$ systemctl is-active nginx
active

Checking a state in scripts

The is-active, is-enabled, and is-failed commands print a single word per unit and set the exit status.

systemctl is-active nginx cron
systemctl is-enabled nginx ssh
systemctl is-active --quiet nginx && echo "nginx is up"

The exit status is 0 only if all listed units are in the asked state. With --quiet, nothing is printed, which suits conditions in scripts. Being active and being enabled are independent: a service can run without being enabled at boot, and an enabled service can be stopped.

$ systemctl is-active nginx cron
active
active
$ systemctl is-enabled nginx ssh
enabled
enabled
$ systemctl is-active --quiet nginx && echo "nginx is up"
nginx is up

Enabling services at boot

Starting a service does not make it start at the next boot. The enable command does that, and disable reverses it.

sudo systemctl disable nginx
systemctl is-enabled nginx
sudo systemctl enable nginx
sudo systemctl disable --now cron
systemctl is-active cron
sudo systemctl enable --now cron

Enabling creates the symbolic links named in the [Install] section of the unit file; here, a link in multi-user.target.wants. On Debian, services that also have an old SysV init script are synchronized with it, which explains the extra lines. The --now option also starts or stops the service, so one command does both.

$ sudo systemctl disable nginx
Synchronizing state of nginx.service with SysV service script with /lib/systemd/systemd-sysv-install.
Executing: /lib/systemd/systemd-sysv-install disable nginx
Removed "/etc/systemd/system/multi-user.target.wants/nginx.service".
$ systemctl is-enabled nginx
disabled
$ sudo systemctl enable nginx
Synchronizing state of nginx.service with SysV service script with /lib/systemd/systemd-sysv-install.
Executing: /lib/systemd/systemd-sysv-install enable nginx
Created symlink /etc/systemd/system/multi-user.target.wants/nginx.service → /lib/systemd/system/nginx.service.
$ sudo systemctl disable --now cron
Synchronizing state of cron.service with SysV service script with /lib/systemd/systemd-sysv-install.
Executing: /lib/systemd/systemd-sysv-install disable cron
Removed "/etc/systemd/system/multi-user.target.wants/cron.service".
$ systemctl is-active cron
inactive
$ sudo systemctl enable --now cron
Synchronizing state of cron.service with SysV service script with /lib/systemd/systemd-sysv-install.
Executing: /lib/systemd/systemd-sysv-install enable cron
Created symlink /etc/systemd/system/multi-user.target.wants/cron.service → /lib/systemd/system/cron.service.

Listing unit files

The list-unit-files command lists installed unit files with their enablement state. Unit names or patterns restrict the list.

systemctl list-unit-files nginx.service cron.service ssh.service apt-daily.service systemd-journald.service cryptdisks.service

A static unit has no [Install] section and cannot be enabled; it is started by other units or by timers, as apt-daily.service is. A masked unit cannot be started at all. The PRESET column shows the distribution's default.

$ systemctl list-unit-files nginx.service cron.service ssh.service apt-daily.service systemd-journald.service cryptdisks.service
UNIT FILE                STATE   PRESET
apt-daily.service        static  -
cron.service             enabled enabled
cryptdisks.service       masked  enabled
nginx.service            enabled enabled
ssh.service              enabled enabled
systemd-journald.service static  -

6 unit files listed.

Viewing a unit file

The cat command prints a unit file together with all drop-in files that modify it.

systemctl cat nginx

The [Unit] section holds the description and the ordering dependencies, [Service] defines how the service is started, reloaded, and stopped, and [Install] says where the service is linked when it is enabled. Package unit files live in /lib/systemd/system; local ones in /etc/systemd/system take precedence.

$ systemctl cat nginx
# /lib/systemd/system/nginx.service
# Stop dance for nginx
# =======================
#
# ExecStop sends SIGQUIT (graceful stop) to the nginx process.
# If, after 5s (--retry QUIT/5) nginx is still running, systemd takes control
# and sends SIGTERM (fast shutdown) to the main process.
# After another 5s (TimeoutStopSec=5), and if nginx is alive, systemd sends
# SIGKILL to all the remaining processes in the process group (KillMode=mixed).
#
# nginx signals reference doc:
# http://nginx.org/en/docs/control.html
#
[Unit]
Description=A high performance web server and a reverse proxy server
Documentation=man:nginx(8)
After=network-online.target remote-fs.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=/usr/sbin/nginx -g 'daemon on; master_process on;' -s reload
ExecStop=-/sbin/start-stop-daemon --quiet --stop --retry QUIT/5 --pidfile /run/nginx.pid
TimeoutStopSec=5
KillMode=mixed

[Install]
WantedBy=multi-user.target

Creating a service

A program becomes a service with a short unit file. This one runs a script that prints a greeting every ten seconds.

hello.service
[Unit]
Description=Hello demo service
After=network.target

[Service]
ExecStart=/usr/local/bin/hello.sh
Restart=on-failure
User=jano

[Install]
WantedBy=multi-user.target

Restart=on-failure restarts the script if it exits with an error, and User=jano runs it without root privileges. The WantedBy line makes enable start it at boot.

sudo cp hello.service /etc/systemd/system/
sudo systemctl daemon-reload
sudo systemctl enable --now hello
systemctl status hello

After adding or changing unit files, daemon-reload makes systemd read them again. The standard output of the script goes to the journal, and its lines appear at the bottom of the status.

$ sudo cp hello.service /etc/systemd/system/
$ sudo systemctl daemon-reload
$ sudo systemctl enable --now hello
Created symlink /etc/systemd/system/multi-user.target.wants/hello.service → /etc/systemd/system/hello.service.
$ systemctl status hello
● hello.service - Hello demo service
     Loaded: loaded (/etc/systemd/system/hello.service; enabled; preset: enabled)
     Active: active (running) since Fri 2026-10-23 13:37:53 CEST; 11s ago
   Main PID: 399 (hello.sh)
      Tasks: 2 (limit: 2811)
     Memory: 576.0K
        CPU: 4ms
     CGroup: /system.slice/hello.service
             ├─399 /bin/bash /usr/local/bin/hello.sh
             └─402 sleep 10

Oct 23 13:37:53 webserver systemd[1]: Started hello.service - Hello demo service.
Oct 23 13:37:53 webserver hello.sh[399]: Hello number 1
Oct 23 13:38:03 webserver hello.sh[399]: Hello number 2

Overriding settings with a drop-in

Instead of editing a unit file, put changes in a drop-in file in the unit.d directory. Here, the drop-in sets an environment variable for the hello service.

override.conf
[Service]
Environment=GREETING=Ahoj

sudo systemctl edit hello opens an editor, creates /etc/systemd/system/hello.service.d/override.conf, and reloads systemd when you save. Here, the file is created directly, so daemon-reload is needed.

sudo mkdir -p /etc/systemd/system/hello.service.d
sudo cp override.conf /etc/systemd/system/hello.service.d/
sudo systemctl daemon-reload
sudo systemctl restart hello
systemctl cat hello
journalctl -u hello -n 3 --no-hostname

systemctl cat shows the unit file followed by the drop-in. The log confirms that the restarted script uses the new greeting. Drop-ins survive package updates, which replace the files in /lib/systemd/system.

$ sudo mkdir -p /etc/systemd/system/hello.service.d
$ sudo cp override.conf /etc/systemd/system/hello.service.d/
$ sudo systemctl daemon-reload
$ sudo systemctl restart hello
$ systemctl cat hello
# /etc/systemd/system/hello.service
[Unit]
Description=Hello demo service
After=network.target

[Service]
ExecStart=/usr/local/bin/hello.sh
Restart=on-failure
User=jano

[Install]
WantedBy=multi-user.target

# /etc/systemd/system/hello.service.d/override.conf
[Service]
Environment=GREETING=Ahoj
$ journalctl -u hello -n 3 --no-hostname
Oct 23 13:38:04 systemd[1]: Stopped hello.service - Hello demo service.
Oct 23 13:38:04 systemd[1]: Started hello.service - Hello demo service.
Oct 23 13:38:04 hello.sh[432]: Ahoj number 1

Failed units

This one-shot service runs a backup script that fails when the backup disk is not mounted.

sudo cp backup.service /etc/systemd/system/
sudo systemctl daemon-reload
sudo systemctl start backup
systemctl --failed
systemctl is-system-running
systemctl status backup
sudo systemctl reset-failed backup

systemctl start reports the failure and suggests where to look. systemctl --failed lists all failed units, and the system state becomes degraded. The status shows the exit code and, in the journal lines, the error message of the script. After the problem is fixed or understood, reset-failed clears the failed state.

$ sudo cp backup.service /etc/systemd/system/
$ sudo systemctl daemon-reload
$ sudo systemctl start backup
Job for backup.service failed because the control process exited with error code.
See "systemctl status backup.service" and "journalctl -xeu backup.service" for details.
$ systemctl --failed
  UNIT           LOAD   ACTIVE SUB    DESCRIPTION
● backup.service loaded failed failed Nightly backup

LOAD   = Reflects whether the unit definition was properly loaded.
ACTIVE = The high-level unit activation state, i.e. generalization of SUB.
SUB    = The low-level unit activation state, values depend on unit type.
1 loaded units listed.
$ systemctl is-system-running
degraded
$ systemctl status backup
× backup.service - Nightly backup
     Loaded: loaded (/etc/systemd/system/backup.service; static)
     Active: failed (Result: exit-code) since Fri 2026-10-23 13:38:04 CEST; 17ms ago
    Process: 462 ExecStart=/usr/local/bin/backup.sh (code=exited, status=1/FAILURE)
   Main PID: 462 (code=exited, status=1/FAILURE)
        CPU: 3ms

Oct 23 13:38:04 webserver systemd[1]: Starting backup.service - Nightly backup...
Oct 23 13:38:04 webserver backup.sh[462]: backup disk is not mounted
Oct 23 13:38:04 webserver systemd[1]: backup.service: Main process exited, code=exited, status=1/FAILURE
Oct 23 13:38:04 webserver systemd[1]: backup.service: Failed with result 'exit-code'.
Oct 23 13:38:04 webserver systemd[1]: Failed to start backup.service - Nightly backup.
$ sudo systemctl reset-failed backup

Unit properties

The show command prints unit properties in a key=value form that is easy to process.

systemctl show nginx -p MainPID -p ActiveState -p SubState -p NRestarts
systemctl show hello --value -p Environment

The -p option selects properties; without it, show prints several hundred of them. --value prints only the value, for example to store it in a shell variable. NRestarts counts how often systemd restarted the service automatically.

$ systemctl show nginx -p MainPID -p ActiveState -p SubState -p NRestarts
MainPID=133
NRestarts=0
ActiveState=active
SubState=running
$ systemctl show hello --value -p Environment
GREETING=Ahoj

Masking a unit

Disabling a service does not prevent other units or an administrator from starting it. Masking does.

sudo systemctl mask cron
sudo systemctl start cron
systemctl is-enabled cron
sudo systemctl unmask cron

mask links the unit name to /dev/null, so systemd refuses to start it in any way. Masking is used for services that must not run, for example a second firewall service that conflicts with the one in use. unmask removes the link.

$ sudo systemctl mask cron
Created symlink /etc/systemd/system/cron.service → /dev/null.
$ sudo systemctl start cron
Failed to start cron.service: Unit cron.service is masked.
$ systemctl is-enabled cron
masked
$ sudo systemctl unmask cron
Removed "/etc/systemd/system/cron.service".

Dependencies

The list-dependencies command shows the units that a unit pulls in.

systemctl list-dependencies multi-user.target --no-pager | head -n 8

multi-user.target is the normal boot target of a server, and the enabled services are its dependencies. A filled dot marks an active unit, and an empty dot an inactive one. The --reverse option shows which units depend on the given unit.

$ systemctl list-dependencies multi-user.target --no-pager | head -n 8
multi-user.target
● ├─cron.service
● ├─dbus.service
○ ├─e2scrub_reap.service
● ├─hello.service
● ├─nginx.service
● ├─ssh.service
● ├─systemd-ask-password-wall.path

Timers

systemd timers are an alternative to cron jobs. The list-timers command lists them with their next run time.

systemctl list-timers --all

Each timer activates a service of the same name. The columns show when the timer fires next, how much time is left, and when it last ran. The --all option includes inactive timers such as fstrim.timer, which is not used in this container.

$ systemctl list-timers --all
NEXT                         LEFT           LAST PASSED UNIT                         ACTIVATES
Fri 2026-10-23 13:52:45 CEST 14min left     -    -      systemd-tmpfiles-clean.timer systemd-tmpfiles-clean.service
Fri 2026-10-23 18:14:20 CEST 4h 36min left  -    -      apt-daily.timer              apt-daily.service
Sat 2026-10-24 00:00:00 CEST 10h left       -    -      dpkg-db-backup.timer         dpkg-db-backup.service
Sat 2026-10-24 06:42:07 CEST 17h left       -    -      apt-daily-upgrade.timer      apt-daily-upgrade.service
Sun 2026-10-25 03:10:21 CET  1 day 14h left -    -      e2scrub_all.timer            e2scrub_all.service
-                            -              -    -      fstrim.timer                 fstrim.service

6 timers listed.

Service logs

systemctl manages units, while journalctl reads their logs.

journalctl -u nginx -n 4 --no-hostname

The -u option selects the unit and -n the number of lines. The log shows the restart from the earlier example. Use journalctl -u nginx -f to follow new entries.

$ journalctl -u nginx -n 4 --no-hostname
Oct 23 13:37:52 systemd[1]: nginx.service: Deactivated successfully.
Oct 23 13:37:52 systemd[1]: Stopped nginx.service - A high performance web server and a reverse proxy server.
Oct 23 13:37:52 systemd[1]: Starting nginx.service - A high performance web server and a reverse proxy server...
Oct 23 13:37:52 systemd[1]: Started nginx.service - A high performance web server and a reverse proxy server.

Best practices for systemctl

Source

systemctl manual page

In this article, we have used the systemctl command to inspect and control services, enable them at boot, create a service and a drop-in override, handle failed units, and list timers and dependencies.

Author

My name is Jan Bodnar, and I am a passionate programmer with extensive programming experience. I have been writing programming articles since 2007. To date, I have authored over 1,400 articles and 8 e-books. I possess more than ten years of experience in teaching programming.

List all Linux tutorials.