Linux command: systemctl
You install a web server, edit its configuration, and want it running now and again after every reboot. You need to stop a service to update it, check why one crashed, or find out what starts automatically at boot. On almost every modern Linux system, the single command behind all of this is systemctl. It is the control panel for systemd, the program that starts your services, mounts your disks, and brings the whole system up in the right order. Learn systemctl and you learn how a Linux machine really runs.
1. The Basics
The job of systemctl is to query and control systemd, the system and service manager. When your machine boots, the kernel starts one program, and that program is systemd (running as process ID 1). Everything else, from your SSH server to your database to the mounting of disks, is started and supervised by systemd. systemctl is the tool you use to talk to it.
The key idea is the unit. Systemd does not think in terms of "programs" or "scripts". It thinks in units: small description files that say what to run and how. The most common kind is the service unit, but there are units for mount points, timers, sockets, and more. Almost every systemctl command acts on one or more units.
The right mental model: systemd is the manager that runs everything on the machine, and every manageable thing (a service, a mount, a timer) is a unit.
systemctlis the remote control you point at those units to start, stop, inspect, and enable them.
This article starts with the plainest use and builds up step by step to the crucial difference between starting and enabling a service, checking state, editing units safely, masking, targets, and the surprising truth that "enabling" a service is really just a symbolic link. By the end, managing services on a systemd machine will feel routine.
1.1 The Simplest Possible Use
Run systemctl with no arguments and it lists every unit systemd currently has in memory, in a pager:
$ systemctl
UNIT LOAD ACTIVE SUB DESCRIPTION
bluetooth.service loaded active running Bluetooth service
ssh.service loaded active running OpenBSD Secure Shell server
...
The output opens in less, so you can scroll and search with /. Press q to quit. The three state columns (LOAD, ACTIVE, SUB) tell you whether systemd read the unit correctly, whether it is running, and the exact sub-state such as running or exited.
1.2 Checking One Service: status
The command you will run most often is status. It shows whether a unit is active, whether it starts at boot, its main process, and the last few log lines from the journal, all in one view:
$ systemctl status ssh
● ssh.service - OpenBSD Secure Shell server
Loaded: loaded (/lib/systemd/system/ssh.service; enabled; preset: enabled)
Active: active (running) since Sun 2026-07-12 09:14:02 CEST; 2h ago
Main PID: 812 (sshd)
Tasks: 1 (limit: 18789)
Memory: 5.2M
CGroup: /system.slice/ssh.service
└─812 sshd: /usr/sbin/sshd -D [listener]
Two words on that line matter most. active (running) tells you it is up right now. The word enabled in the Loaded: line tells you it will also start automatically at the next boot. These are two separate facts, and the difference between them is the single most important thing to understand about systemctl, covered in section 5.
1.3 You Usually Need root
Reading state (status, list-units, is-active) works as a normal user. But changing anything, such as starting, stopping, enabling, or disabling a system service, needs administrator rights. Prefix those commands with sudo:
$ systemctl status ssh # reading: no sudo needed
$ sudo systemctl restart ssh # changing: sudo required
If you forget sudo, systemd will often pop up a polkit password prompt instead of failing outright, but on a server over SSH it usually just refuses.
2. Where the Name Comes From
The name is two parts joined together: system plus ctl.
systemctl = SYSTEM + con-TROL
The system part points at systemd, whose own name means "system daemon" (the trailing d is the classic Unix convention for a background daemon, as in sshd or httpd). So systemctl is the tool that controls the system daemon.
The ctl ending is short for control, and it is a house style across the whole systemd family. Once you spot the pattern, a long list of tool names suddenly makes sense:
| Command | Controls |
|---|---|
systemctl |
services and units |
journalctl |
the journal (logs) |
hostnamectl |
the system hostname |
timedatectl |
time, date, and time zone |
loginctl |
user logins and sessions |
systemd-analyze |
boot time and performance |
So systemctl reads as "the control program for the system manager". It is the central member of the family: the others manage one slice of the system, while systemctl manages the units that make up the whole thing.
3. A Short History
To understand systemctl you have to know what it replaced, because systemd changed how Linux starts up more than any other single project in the last twenty years.
For most of Unix history, the first program the kernel started was init, and the traditional design was SysV init: a set of shell scripts under /etc/init.d, run in a fixed order by numbered "runlevels". It worked, but it was slow, because it started services one after another, and fragile, because each service was a hand-written script. You controlled a service with commands like service nginx start or by calling the script directly.
systemd, first released in 2010 by Lennart Poettering and Kay Sievers, set out to replace init entirely. Its big ideas were to describe services as small declarative unit files instead of scripts, to start independent services in parallel for a faster boot, and to track every service's processes reliably using kernel control groups (cgroups). Over the following years nearly every major distribution adopted it, and systemctl became the standard way to manage services.
| Era | Milestone |
|---|---|
| 1980s | SysV init and numbered runlevels set the pattern for booting Unix |
| 2006 | Ubuntu ships Upstart, an early event-based replacement for init |
| 2010 | systemd is released, using declarative units and parallel startup |
| 2014-2015 | Debian, Ubuntu, Fedora, RHEL, Arch, and SUSE adopt systemd as default |
| Today | systemd is the standard init on nearly all mainstream Linux distributions |
The move was not without argument: systemd is large and does much more than the old init, which some administrators dislike. A few distributions (such as Devuan) deliberately avoid it. But on a default modern install, systemctl is how you manage services, and this article is verified against systemd version 255.
4. Simple Use Cases
Four verbs cover almost all day-to-day service work: start, stop, restart, and reload. Each takes one or more unit names. The .service suffix is optional; if you leave off the type, systemd assumes .service.
4.1 Start and Stop a Service
start activates a unit now; stop deactivates it now. Neither affects what happens at the next boot.
$ sudo systemctl start nginx # bring nginx up now
$ sudo systemctl stop nginx # shut nginx down now
These commands are quiet on success: no news is good news. Run systemctl status nginx afterwards, or check the exit code, to confirm the change.
4.2 Restart After a Change: restart
When you change a program's configuration file, restart stops the service and starts it again so the new settings take effect:
$ sudo systemctl restart nginx # full stop, then start
A restart briefly drops the service. For something like a web server that means a short moment where it does not answer, which is where the next command helps.
4.3 Reload Without Dropping the Service: reload
reload asks the running service to re-read its configuration without stopping. It keeps existing connections alive, so a web server or database does not go down:
$ sudo systemctl reload nginx # re-read config, stay up
The catch: a service can only reload if its author wrote support for it. If it cannot, you get an error. When you are unsure which one you need, reload-or-restart reloads when possible and otherwise restarts:
$ sudo systemctl reload-or-restart nginx
Back to top5. The Most Important Distinction: start versus enable
This is the one thing everybody gets wrong at first, so it gets its own section. Starting a service and enabling it are two different actions, and doing one does not do the other.
| Command | Effect | Survives reboot? |
|---|---|---|
systemctl start X |
Runs X right now | No |
systemctl enable X |
Sets X to start at boot | Yes, but does not start it now |
If you only start a service, it runs until the next reboot and then never comes back. If you only enable it, it is scheduled for the next boot but is not running yet. Almost always you want both, which is why the --now flag exists:
$ sudo systemctl enable --now nginx # enable AND start in one step
$ sudo systemctl disable --now nginx # disable AND stop in one step
5.1 Enable, Disable, and the --now Shortcut
enable creates the link that makes a service start at boot; disable removes it. On their own they do not touch the currently running state:
$ sudo systemctl enable nginx # will start at boot; not running yet
$ sudo systemctl disable nginx # will not start at boot; still running now
Remember the rule: start/stop is about now, enable/disable is about boot, and --now bridges the two.
5.2 Asking Yes-or-No Questions: is-active, is-enabled, is-failed
These three commands answer a single question and print one word, which makes them ideal inside scripts. They set the exit code too, so you can test them directly:
$ systemctl is-active nginx # active / inactive / failed
$ systemctl is-enabled nginx # enabled / disabled / static / masked
$ systemctl is-failed nginx # failed / active
A word you will meet here is static: it means the unit has no install section to enable, usually because something else pulls it in as a dependency. You cannot enable a static unit, and you do not need to.
5.3 Listing What Exists and What Runs
Two list commands look similar but answer different questions. list-units shows units currently in memory (loaded, mostly active); list-unit-files shows every unit installed on disk and whether each is enabled:
$ systemctl list-units --type=service # what is loaded now
$ systemctl list-units --type=service --state=running
$ systemctl list-unit-files --type=service # everything installed, with state
$ systemctl --failed # only units that failed
The --failed shortcut (equal to --state=failed) is the fastest health check on a server: an empty list means nothing is broken.
6. Advanced Use Cases
6.1 Editing a Unit the Right Way: systemctl edit
Never edit a vendor's unit file under /lib/systemd/system directly; a package update will overwrite it. Instead, use systemctl edit, which creates a small drop-in override file that only changes the settings you name and leaves the original alone:
$ sudo systemctl edit nginx # opens an override drop-in in an editor
This writes to /etc/systemd/system/nginx.service.d/override.conf. Your lines there win over the vendor's. To replace the whole file instead of layering on top, use systemctl edit --full. After editing, systemd needs to re-read its configuration, which is the next command.
6.2 Reloading systemd Itself: daemon-reload
There are two very different kinds of "reload", and mixing them up is a classic trap:
systemctl reload nginxtells the nginx service to re-read its own config.systemctl daemon-reloadtells systemd itself to re-read all unit files.
Whenever you add, edit, or remove a unit file, run daemon-reload so systemd notices the change, then restart the affected service:
$ sudo systemctl daemon-reload # systemd re-reads unit files
$ sudo systemctl restart nginx # apply the new unit definition
If you edit a unit and your changes seem ignored, a missing daemon-reload is the usual reason.
6.3 Writing Your Own Service Unit
Sooner or later you want to run your own program as a managed service, so it starts at boot, restarts on failure, and logs to the journal like any other unit. You do this by writing a small unit file under /etc/systemd/system/. A service unit has three sections:
$ sudo nano /etc/systemd/system/myapp.service
[Unit]
Description=My Application
After=network.target
[Service]
ExecStart=/opt/myapp/app.sh
Restart=on-failure
User=myapp
[Install]
WantedBy=multi-user.target
Each section has a clear job:
| Section | Answers |
|---|---|
[Unit] |
What is it, and when should it start relative to others |
[Service] |
What to run (ExecStart), as whom, and how to restart it |
[Install] |
Which target pulls it in when you enable it |
After creating the file, tell systemd about it and turn it on:
$ sudo systemctl daemon-reload # systemd notices the new file
$ sudo systemctl enable --now myapp # start it now and at every boot
The Restart=on-failure line is one of the reasons to prefer a real unit over a hand-run script: systemd will bring your program back up if it crashes.
6.4 Controlling Order and Requirements
The [Unit] section is where you say how a unit relates to others, and two different ideas are easy to confuse: ordering (when) and requirement (whether):
| Directive | Meaning |
|---|---|
After= |
Ordering only: start this unit after the named one |
Before= |
Ordering only: start this unit before the named one |
Requires= |
Hard requirement: if the named unit fails or stops, this one stops too |
Wants= |
Soft requirement: pull the named unit in, but do not fail if it does not start |
The trap is that ordering and requirement are separate. Requires= says the other unit must be there, but it does not say to wait for it; you almost always pair it with After= to also get the order right:
[Unit]
Requires=postgresql.service
After=postgresql.service
Wants= is the gentler and more common choice: it is what enable writes for you behind the scenes, and it keeps one failing dependency from dragging your service down with it.
6.5 Understanding Dependencies: list-dependencies
Once units depend on each other, you often need to see the whole tree. list-dependencies shows what a unit pulls in, and with --reverse, what depends on it:
$ systemctl list-dependencies nginx # what nginx pulls in
$ systemctl list-dependencies --reverse nginx # what depends on nginx
This is how you answer "why does this service start?" and "what breaks if I stop that one?".
6.6 Hardening a Service: Sandboxing
One of systemd's most underused powers is that the [Service] section can lock a service into a sandbox, shrinking what a compromised program can reach. You add these as plain directives, no container needed:
| Directive | Effect |
|---|---|
ProtectSystem=strict |
Mount the whole filesystem read-only for the service |
ProtectHome=true |
Hide /home and /root from the service |
PrivateTmp=true |
Give the service its own private /tmp |
NoNewPrivileges=true |
Stop the service ever gaining more privileges (no setuid) |
PrivateDevices=true |
Hide real hardware device nodes |
You can measure how well a service is locked down with a built-in scanner that scores each unit and suggests what to add:
$ systemd-analyze security nginx # exposure score and per-setting advice
For a service exposed to the internet, a few of these lines can turn a full-system compromise into a contained one. Add them through systemctl edit so they survive updates.
6.7 Seeing the Real Unit File: cat and show
systemctl cat prints the exact unit file systemd loaded, including any drop-ins layered on top, so you see the effective definition:
$ systemctl cat nginx # the unit file plus any overrides
systemctl show goes further and dumps every resolved property as KEY=VALUE, which is how you check what a setting actually became:
$ systemctl show nginx -p MemoryCurrent -p ExecStart
6.8 Masking: The Strongest "Off" Switch
disable stops a service starting at boot, but something else can still pull it in as a dependency, or you can still start it by hand. mask is the absolute block: it makes the unit impossible to start at all, by anyone, until you unmask it:
$ sudo systemctl mask nginx # nginx cannot be started at all now
$ sudo systemctl unmask nginx # allow it again
Use masking when you truly want a service dead, for example replacing a distribution's default web server with your own. Under the hood, masking links the unit to /dev/null, which section 7 explains.
6.9 Targets: Grouping Units into System States
A target is a special unit that groups other units, used to describe a whole system state. Targets are systemd's replacement for the old runlevels:
| Target | Rough old runlevel | Meaning |
|---|---|---|
multi-user.target |
3 | Full system, networking, no graphical login |
graphical.target |
5 | Everything in multi-user, plus a desktop |
rescue.target |
1 | Single-user rescue shell, minimal services |
emergency.target |
- | Barest shell, almost nothing started |
You can see the boot default, change it, or switch the running system into another target:
$ systemctl get-default # e.g. graphical.target
$ sudo systemctl set-default multi-user.target # boot without a desktop next time
$ sudo systemctl isolate rescue.target # switch to rescue mode now
6.10 Reboot, Power Off, and Suspend
Because systemd manages the whole system state, it also owns shutdown and sleep. These verbs replace the older separate commands:
$ systemctl reboot # restart the machine
$ systemctl poweroff # shut down and power off
$ systemctl suspend # sleep to RAM
$ systemctl hibernate # sleep to disk
Back to top7. Something Most Users Do Not Know
7.1 "Enabling" a Service Is Just a Symbolic Link
The word enable sounds like it flips a switch deep inside systemd. It does something much simpler: it creates a symbolic link. A unit file has an [Install] section that says which target wants it, for example WantedBy=multi-user.target. When you enable the unit, systemd creates a symlink to it inside that target's .wants directory:
$ sudo systemctl enable nginx
Created symlink /etc/systemd/system/multi-user.target.wants/nginx.service
→ /lib/systemd/system/nginx.service.
At boot, systemd starts multi-user.target, sees the symlinks in its .wants directory, and starts each linked unit. That is the entire mechanism. disable simply deletes the link. Once you know this, the whole enable/disable system stops being magic and becomes files you can list with ls.
Enabling is not a hidden database flag. It is a symlink in a target's
.wantsdirectory. Boot is systemd following those links. Everything you can enable, you could create by hand withln -s(though you should not; letsystemctldo it).
7.2 Masking Is a Link to Nowhere
Masking uses the same trick in reverse. When you mask a unit, systemd links its name to /dev/null:
$ sudo systemctl mask nginx
Created symlink /etc/systemd/system/nginx.service → /dev/null.
Because the unit now "is" the empty device, systemd sees nothing to run and refuses every attempt to start it, whether by you, at boot, or as a dependency. This is why masking is stronger than disabling: a disabled unit still exists and can be started; a masked one points at nothing.
7.3 systemctl Does Not Touch Services Directly: It Speaks D-Bus
When you run systemctl restart nginx, the systemctl program does not start or stop anything itself. It sends a message to systemd (the process with PID 1) over D-Bus, the standard message bus Linux uses for programs to talk to each other. Systemd receives the request, checks whether you are allowed to make it, does the actual work, updates the unit's state, and writes the result to the journal.
This design explains several things you may have noticed:
- A polkit password dialog can pop up on a desktop, because D-Bus lets systemd ask an authorization service whether to permit the action.
- You can manage a machine remotely at the protocol level with
systemctl -H user@host, because the request is just a message that can travel. systemctlis really a thin client: the intelligence lives in the systemd manager it talks to, not in the command.
7.4 The Same Unit Can Live in Three Places
Systemd looks for unit files in several directories, and when the same unit name appears in more than one, a fixed order of precedence decides which wins. From highest priority to lowest:
| Directory | Purpose | Priority |
|---|---|---|
/etc/systemd/system/ |
Your local changes and custom units | Highest (admin wins) |
/run/systemd/system/ |
Runtime units, gone after reboot | Middle |
/usr/lib/systemd/system/ |
Vendor and package units | Lowest |
This ranking is the whole reason drop-ins and systemctl edit work: your file in /etc always beats the package's file in /usr/lib, so an update to the package cannot silently undo your change. On Debian and Ubuntu, /lib/systemd/system is the same location as /usr/lib/systemd/system. To see which file actually won, use systemctl cat.
7.5 You Have Your Own Personal systemd
Systemd runs a second, separate instance just for your login: the user manager. It manages services that belong to you and do not need root, such as a desktop component or a personal backup timer. You control it by adding --user to any command:
$ systemctl --user status # your own units
$ systemctl --user start myapp # a service you own, no sudo
$ systemctl --user enable --now myapp
User units live under ~/.config/systemd/user/. This explains a common confusion: a service you started with systemctl --user will not show up under a plain systemctl status, because that queries the system manager, not yours. If you want a user service to keep running after you log out, enable lingering for your account with loginctl enable-linger.
7.6 Every Manageable Thing Is a Unit, Not Just Services
Because people use systemctl mostly for services, it is easy to forget that systemd manages many other unit types the same way. The suffix tells you the type:
| Suffix | Unit type | Manages |
|---|---|---|
.service |
Service | A daemon or program |
.socket |
Socket | A port or socket that starts a service on first use |
.timer |
Timer | A scheduled job, a modern replacement for cron |
.mount |
Mount | A filesystem mount point |
.target |
Target | A group of units and a system state |
So systemctl list-timers shows your scheduled jobs, and systemctl start something.mount mounts a filesystem. The same start, stop, enable, and status verbs work on all of them.
7.7 Where systemctl Stops, Other Tools Take Over
systemctl controls units; a few neighbours pick up where it ends:
journalctl -u nginxreads a unit's logs;systemctl statusonly shows the last few lines.systemd-analyze blameshows which units slowed your boot the most, andsystemd-analyze critical-chainshows the ordered chain that gated it.systemctl list-timerslists scheduled timers; edit them as.timerunits.loginctlmanages user sessions and lingering;hostnamectlandtimedatectlhandle the hostname and clock.
Knowing this boundary keeps you efficient: systemctl to control a service, journalctl to read why it failed.
8. Best Practices
- Decide "now" and "at boot" on purpose. For a service you want permanently, use
enable --now. For a one-off, use plainstart. Being clear about which you mean prevents surprises after a reboot. - Prefer
reloadoverrestartwhen it exists. A reload keeps connections alive; a restart drops them for a moment. Fall back toreload-or-restartwhen unsure. - Never edit vendor unit files directly. Use
systemctl editto add a drop-in override, so a package update cannot wipe your changes. - Run
daemon-reloadafter any unit-file change. Otherwise systemd keeps using the old definition and your edits appear to do nothing. - Give your own program a real unit. A short
[Unit]/[Service]/[Install]file withRestart=on-failurebeats a hand-run script: it starts at boot, restarts on crash, and logs to the journal. - Sandbox internet-facing services. Add a few lines such as
ProtectSystem=strict,PrivateTmp=true, andNoNewPrivileges=true, and check the result withsystemd-analyze security. - Check
systemctl --failedregularly. On a server it is the fastest way to spot a service that quietly died. - Use
systemctl catto see the truth. It shows the real, effective unit including overrides, which avoids guessing about which file is in force. - Read the manual when unsure. The behaviour is fully documented;
man systemctlandman systemd.unitare the reference.
$ man systemctl # the command and all its verbs
$ man systemd.service # how to write a service unit
$ systemctl --help # a quick summary of the commands
Back to top9. Common Mistakes
9.1 Myth versus Reality
| Myth | Reality |
|---|---|
"systemctl start makes a service permanent." |
It runs the service now only. Use enable (or enable --now) to make it start at boot. |
"enable also starts the service." |
By itself it only schedules it for the next boot. Add --now to start it as well. |
"reload and daemon-reload are the same." |
reload re-reads the service's own config; daemon-reload re-reads systemd's unit files. They solve different problems. |
"disable guarantees a service can never run." |
A disabled unit can still be started by hand or pulled in as a dependency. Use mask for an absolute block. |
"Editing the file in /lib/systemd/system is fine." |
A package update overwrites it. Use systemctl edit to keep changes in a safe drop-in. |
9.2 Other Traps to Avoid
- Forgetting
daemon-reloadafter editing a unit. Systemd caches unit files in memory. Until you reload, it uses the old version and your edit seems ignored. - Restarting when a reload would do. On a busy web or database server, a needless
restartdrops live connections. Check whetherreloadis supported first. - Looking for a user service in the system view. A unit started with
--userwill not appear under a plainsystemctl status. Add--userto see it. - Reading logs with the wrong tool.
systemctl statusshows only the last lines. For the full history of a unit, switch tojournalctl -u name. - Confusing the service name with the program name. The unit may be
ssh.servicewhile the process issshd. Usesystemctl list-unitsor tab completion to find the exact unit name.
10. Summary
The systemctl command is the control panel for systemd, and systemd runs everything on a modern Linux machine. Learn a handful of verbs and one key distinction, and managing services becomes calm and predictable instead of guesswork.
systemctlqueries and controls systemd, the manager that starts every service, mount, and timer as a unit.- The name means "system control", part of the systemd family alongside
journalctlandtimedatectl. - It replaced SysV init scripts with declarative unit files and parallel startup, arriving with systemd in 2010.
- Manage a running service with
start,stop,restart, andreload; check it withstatus. - The crucial distinction:
start/stopact now,enable/disableact at boot, and--nowdoes both. - Ask yes-or-no with
is-active,is-enabled, andis-failed; list state withlist-units,list-unit-files, and--failed. - Write your own service as a small
[Unit]/[Service]/[Install]file, order it withAfter=/Before=, and require others withWants=/Requires=. - Harden a service in its
[Service]section with sandboxing (ProtectSystem=,PrivateTmp=,NoNewPrivileges=), scored bysystemd-analyze security. - Edit units safely with
systemctl edit(a drop-in), then rundaemon-reloadso systemd notices. - Block a service completely with
mask; group units and system states with targets likemulti-user.target. - Under the hood, enabling is a symlink in a target's
.wantsdirectory, masking is a symlink to/dev/null, andsystemctlreaches systemd over D-Bus. - A unit in
/etc/systemd/systemalways overrides the vendor copy in/usr/lib/systemd/system, which is what makes drop-ins survive updates. - You have a second, personal systemd reached with
--user, and every manageable thing (service, timer, mount, target) is a unit.
This is the quick reference worth keeping:
systemctl status nginx is it running, enabled, and healthy
systemctl start nginx run it now
systemctl stop nginx shut it down now
systemctl restart nginx full stop then start
systemctl reload nginx re-read config, stay up
systemctl enable nginx start at boot (not now)
systemctl enable --now nginx start at boot AND now
systemctl disable --now nginx stop now AND at boot
systemctl is-active nginx one-word running check
systemctl is-enabled nginx one-word boot check
systemctl --failed list everything that failed
systemctl list-units --type=service loaded services
systemctl list-unit-files every installed unit and its state
systemctl edit nginx safe drop-in override
systemctl daemon-reload reload unit files after editing
systemctl cat nginx show the effective unit file
systemctl mask nginx block a service completely
systemctl list-dependencies nginx what it needs
systemd-analyze security nginx score a service's sandboxing
systemctl --user status your own user services
systemctl reboot restart the machine
Managing services well is one of the quiet skills that separates a calm server administrator from a stressed one: the difference between a site that comes back cleanly after every reboot and one that silently forgets to start a service at the worst possible moment. If your servers have services that do not survive a restart, changes that never seem to take effect, or a boot that no one fully understands, it pays to have systemd set up correctly before the next incident, so the machine behaves exactly as you expect.
Back to top

Peter is a Joomla specialist and a Linux admin for fast, secure and scalable websites.


