The command most people want is systemctl list-units --type=service. The command they usually needed is systemctl list-unit-files --type=service. The first shows what is loaded in memory right now; the second shows everything installed on the box and whether it starts at boot.
That distinction is the entire reason this question keeps getting asked. A service can be installed and disabled, installed and enabled but crashed, enabled and running, or masked so hard that systemd refuses to start it even if you ask nicely. One list does not cover all of those states, and picking the wrong list is how people conclude a service is not installed when it is sitting right there, disabled.
Table of contents
- Three different questions, three different commands
- List the services that are running right now
- List every installed service and whether it starts on boot
- The name in one list is not always the name in the other
- Filtering and formatting for scripts
- When systemctl is not the whole story
- Reading a service that is failing to start
- How this fits the rest of the stack
- FAQ
Three different questions, three different commands
Before typing anything, decide which question you are actually asking. They look similar and they return different sets of rows.
- What is running right now? That is the runtime view. Units that systemd has loaded and started in this boot.
- What is installed and what starts on boot? That is the unit-file view. It includes things that have never run.
- What failed? That is the narrow view, and it is the one worth checking first when something is broken.
The runtime view lives behind list-units. The install view lives behind list-unit-files. They come from different places — one from the in-memory unit table, one from scanning /lib/systemd/system and /etc/systemd/system — so a unit can appear in the second and be entirely missing from the first.
# running right now
systemctl list-units --type=service --state=running
# everything installed, with its boot state
systemctl list-unit-files --type=service
# only the ones that are broken
systemctl --failed
If you remember one thing from this page, make it the third command. systemctl --failed is the fastest path from a vague report to a specific unit name, and it costs nothing to run.
List the services that are running right now
list-units is the default subcommand, which is why bare systemctl dumps an enormous table of every unit type on the system: services, sockets, timers, mounts, devices, slices. Filter it down.
systemctl list-units --type=service
systemctl list-units --type=service --state=running
systemctl list-units --type=service --state=failed
systemctl list-units --type=service --all
The --all flag is the one that surprises people. Without it, systemd hides units that are loaded but inactive, so a service you just stopped disappears from the list entirely and looks uninstalled. With it, you see the inactive rows too.
The columns are worth reading properly rather than skimming. LOAD says whether systemd could parse the unit file. ACTIVE and SUB say what the process is doing — active/running for a normal daemon, active/exited for a one-shot that did its job and quit, activating/start-pre for something stuck in a pre-start hook. A one-shot showing active/exited is healthy. A daemon showing the same thing is not.
Paging gets in the way when you want to grep the output, and systemd sends everything through a pager by default. Turn it off with --no-pager, and drop the legend footer with --no-legend when you are feeding the result to another command.
List every installed service and whether it starts on boot
This is the view that answers can I stop worrying about this thing coming back after a reboot.
systemctl list-unit-files --type=service
systemctl list-unit-files --type=service --state=enabled
systemctl list-unit-files --type=service --state=disabled
The STATE column has more values than the two obvious ones, and the extra ones matter:
enabled— has a symlink in a.wantsdirectory, so it starts at boot.disabled— installed, no symlink, will not start on its own.static— has no[Install]section at all. It cannot be enabled directly; something else pulls it in as a dependency.masked— symlinked to/dev/null. systemd will refuse to start it even on an explicit request. This is the strongest off switch there is, and it is the one people forget they used three months ago.generated— created on the fly by a generator, usually a SysV init script or an/etc/fstabentry that systemd wrapped for you.
A unit showing static is not broken and does not need enabling. It is a building block. Trying to systemctl enable it produces a message about no installation config, which reads like an error and is not one.
The name in one list is not always the name in the other
This is the trap that eats the most time. Distributions inherited service names from SysV init and systemd added its own, and the two do not always match. On Debian and Ubuntu the SSH daemon is ssh.service; on Red Hat family systems it is sshd.service. The system logger is rsyslog on some boxes and syslog on others, with an alias pointing one at the other.
So systemctl status sshd returning unit not found does not mean SSH is missing. It means you guessed the wrong name on that distribution. Search instead of guessing:
systemctl list-units --type=service | grep -i ssh
systemctl list-unit-files --type=service | grep -i ssh
# resolve an alias to its real unit
systemctl show -p Names ssh.service
Tab completion works on unit names and is faster than any of this, but it only completes units systemd knows about, which puts you back in the same trap on a fresh box where the package is installed but nothing has loaded it yet.
Filtering and formatting for scripts
Parsing the human table is fragile. Column widths shift, the legend appears and disappears, and the unicode status bullet in the first column breaks naive field splitting. Use the flags that exist for this.
# plain machine-readable list of running service names
systemctl list-units --type=service --state=running \
--no-pager --no-legend --plain | awk '{print $1}'
# just the names of enabled units
systemctl list-unit-files --type=service --state=enabled \
--no-pager --no-legend | awk '{print $1}'
# is one specific unit enabled? exit code, no parsing
systemctl is-enabled nginx.service
systemctl is-active nginx.service
is-active and is-enabled are the right tools inside a health check or a deploy script. They print one word and set a useful exit code, which means you can write if systemctl is-active --quiet nginx; then and be done, instead of grepping a table and hoping the format holds across distro upgrades.
For anything more structured, systemctl show <unit> prints every property as Key=Value lines, and -p narrows it to the ones you asked for. It is verbose but it is stable, which is the property you want in automation.
When systemctl is not the whole story
Plenty of things that behave like services are invisible to systemctl list-units, and assuming otherwise leads to a confident wrong answer.
- Per-user units.
systemctl --user list-units --type=serviceis a separate tree with its own enabled state. Desktop agents and some developer tooling live here. - Containers. A process inside a container is a child of the container runtime as far as the host is concerned. The host sees
docker.serviceorcontainerd.service, not your API. Ask the runtime:docker ps,docker compose ps. - Timers. A job that runs every ten minutes is often a
.timerunit that activates a.serviceand exits. It will not show as running between firings. Checksystemctl list-timers. - Old-style init scripts. On a box still carrying SysV scripts,
service --status-allshows what the generator picked up, and it is a useful cross-check.
Once you have the unit name, the next command is almost always the log, not another list. journalctl -u nginx.service -n 100 --no-pager gets you the last hundred lines for that unit, and -f follows it. Adding -p err filters to error priority and above, which turns a wall of startup chatter into the three lines that matter.
Reading a service that is failing to start
The list tells you what is broken. It does not tell you why. The sequence that works, in order, is short:
systemctl --failedto get the exact unit name.systemctl status <unit>for the exit code, the main PID, and the last few log lines inline.journalctl -u <unit> -bfor everything that unit logged this boot.systemctl cat <unit>to read the effective unit file, including any drop-in overrides you forgot about.
That fourth step catches a surprising number of problems. systemctl cat shows the base unit plus every .d/*.conf override concatenated in order, which is the file systemd is actually using. Reading the vendor unit in /lib/systemd/system while an override in /etc/systemd/system/<unit>.d/ is quietly changing ExecStart is a good way to spend an hour debugging the wrong file.
And if you edited a unit file, systemctl daemon-reload before restarting. systemd caches the parsed unit, so without the reload you restart the old definition and conclude your fix did nothing.
How this fits the rest of the stack
Most of this exists because a plain Linux box gives you a process supervisor and nothing else. You get the unit table and the journal, and the job of turning those into an answer is yours. That is fine on one server and tiring across ten. When a service on RunxBuild will not start, the deploy log and the runtime log sit on the same page as the service, and rollback to the previous working deploy is one action rather than a unit-file archaeology session. Services deploy from a GitHub repository with build logs, a live route, environment variables, and metrics attached — see Services on RunxBuild for how that fits together. If you are sizing a move off a VM you are hand-administering, the RunxBuild hosting calculator puts the API, database, storage, and bandwidth line items next to each other so the comparison is a number rather than a feeling.
Useful related references:
- Linux Show Running Services: systemctl Without the Noise
- tar Command in Linux: Create, Extract, List, Compress Archives
- List Users in Linux: Reading /etc/passwd Like a Security Audit
- Services on RunxBuild
FAQ
What is the difference between list-units and list-unit-files?
list-units shows units systemd has loaded into memory during this boot, with their runtime state. list-unit-files scans the unit directories on disk and shows every installed unit with its boot-time state (enabled, disabled, static, masked). A unit that has never run appears in the second list and not the first.
How do I list only running services?
Use systemctl list-units --type=service --state=running. Add --no-pager --no-legend --plain if you are piping the output into another command, and use awk '{print $1}' to get just the unit names.
Why does systemctl status sshd say unit not found?
The service is almost certainly installed under a different name on your distribution. Debian and Ubuntu use ssh.service; Red Hat family systems use sshd.service. Run systemctl list-unit-files --type=service | grep -i ssh to find the real name instead of guessing.
What does a static service state mean?
The unit file has no [Install] section, so it cannot be enabled or disabled directly. It runs when another unit pulls it in as a dependency. This is normal and is not an error, even though systemctl enable on it prints a message that reads like one.
How do I see services that are not managed by systemd?
Containers are invisible to systemctl — ask the runtime with docker ps or docker compose ps. Per-user units need systemctl --user list-units. Scheduled jobs are usually timers, listed with systemctl list-timers. Legacy init scripts show up under service --status-all.