systemctl, loginctl, journalctl, and Every Other *ctl: The systemd Toolbox, Start to Finish

root@rhel:~# systemctl status --failed
root@rhel:~# systemctl status --failed # the first thing you ask a box that came up wrong

systemctl, loginctl, journalctl, and Every Other *ctl
The systemd Toolbox, Start to Finish

systemd is one init system, but the tools are a dozen small ones, and each of them answers a different question. systemctl runs the services. journalctl reads the logs. loginctl owns the sessions nobody thinks about until a tmux session dies at logout. The rest — hostnamectl, timedatectl, localectl, systemd-analyze, resolvectl, networkctl, coredumpctl, busctl — are the small tools that replace the files you used to edit by hand. This is the whole family, in proportion to how much you will actually use each one. Two of them already have their own posts on this site — journalctl (journalctl Beyond -u and –since) and systemd-resolved with networkd (Ubuntu Networking, Untangled) — so those two get the short version here and a pointer, not a second copy.

# which systemd — the version map, because the tools moved
Check before you copy anything. systemctl --version prints the number on the first line, and the number decides which commands exist. RHEL 7 ships systemd 219, RHEL 8 ships 239, RHEL 9 ships 252, RHEL 10 ships 257; Oracle Linux matches RHEL release for release. Ubuntu 22.04 ships 249, 24.04 ships 255, 26.04 ships 259. Most of this post works on all of them. Where it does not, the section says so — systemctl freeze, for example, arrived in systemd 244, so RHEL 9 and 10 have it and RHEL 8 does not; resolvectl did not exist before 239, so on RHEL 7 the old name systemd-resolve is all there is.
One more thing the version decides: the defaults. systemd backports fixes but does not backport behaviour, and the distributions patch the defaults. The clearest case is in the loginctl section below, where the same setting kills your tmux on one distro and leaves it alone on another. When a section says “check the file on the box in front of you”, that is why.
Every systemd argument on the internet assumes a version. The version on the box in front of you is the only one that matters.
# systemctl — the one you live in
Start with the state, not the service. systemctl status with no arguments shows the system state and the units that failed; systemctl --failed lists only the failures, and systemctl is-system-running answers in one word — running, degraded, maintenance, or worse. A degraded box with one failed unit is a different morning from a box that will not finish booting, and this is how you tell which one you have.
The four verbs, and what they actually do. systemctl start <unit> starts it now. stop stops it now. restart stops and starts it. reload asks the running process to re-read its configuration without stopping — which only works if the service implements reload, and silently does nothing useful if it does not. The fifth verb is the one people reach for second: systemctl enable <unit> makes it start at boot, disable stops it starting at boot, and neither touches the running process. Combine them when you mean both: systemctl enable --now <unit> enables and starts in one command.
Mask is the one with teeth. systemctl mask <unit> links the unit to /dev/null, so it cannot be started by hand, by another unit, or by a dependency — not even accidentally. unmask removes the link. Mask is for the service that must never run on this box (a second DHCP client, a conflicting mount); disable is for the service that must not run yet. Do not mask something you only meant to disable — the next person to start it by hand will get an error that says nothing useful.
Never edit a unit file in place. The shipped files in /usr/lib/systemd/system/ belong to the package manager, and the next update overwrites them. systemctl edit <unit> opens a drop-in at /etc/systemd/system/<unit>.d/override.conf instead — a small file with only your changes, which wins over the shipped file and survives updates. systemctl edit --full <unit> copies the whole unit to /etc/systemd/system/ for a full rewrite; use it rarely, because a full copy stops tracking the shipped file entirely. After either, systemctl daemon-reload makes PID 1 re-read the unit files. daemon-reload is not a restart — it changes what systemd knows, not what is running. daemon-reexec is the heavier one: it re-execs PID 1 itself, which is what you do after a systemd package update.
Read before you change. systemctl cat <unit> prints the unit file and every drop-in that applies to it, in the order they apply — the fastest way to see what a unit actually is, as opposed to what you remember it being. systemctl show <unit> -p <Property> prints one property (systemctl show nginx.service -p MainPID -p ActiveState); systemctl show <unit> with no property prints all of them, which is a lot. systemctl list-dependencies <unit> shows what it pulls in; add --reverse to see what pulls it in, and --all to include the targets. systemctl list-units --type=service --state=failed is the long form of --failed; systemctl list-unit-files shows every unit and whether it is enabled, disabled, or static. systemctl list-timers and systemctl list-sockets are the same idea for timers and sockets — and the timer list is where you find the job that runs at 06:00, which is usually the answer to “what changed overnight”.
Targets are the runlevels, renamed. systemctl get-default prints the boot target — multi-user.target on a server, graphical.target on a desktop. systemctl set-default multi-user.target changes what the next boot reaches. systemctl isolate rescue.target drops a running box into rescue mode without rebooting. Targets are units like any other, which is why the same commands work on them.
The user instance is a second systemd. systemctl --user talks to the per-user manager instead of PID 1, with its own units in ~/.config/systemd/user/. User services stop when the user logs out — unless lingering is enabled, which is a loginctl question, in the next-but-one section. systemctl --user enable --now <unit> is how a long-running user service survives a reboot; without linger it does not survive a logout.
Two newer verbs, worth knowing. systemctl freeze <unit> and thaw suspend and resume a unit’s processes in place (systemd 244 and later, so RHEL 9 and 10, not RHEL 8) — a paused service, not a stopped one, which is the right tool when you want to inspect a misbehaving process without killing it. systemctl kill <unit> sends a signal to the main process by default, or to all of the unit’s processes with --kill-who=all — cleaner than finding the PID by hand, because systemd already knows which processes belong to the unit. And systemctl reset-failed clears the failed state after you have fixed the cause; the failed flag does not clear itself.
The unit file you edited in /usr/lib is gone at the next update. The drop-in in /etc is still there. Edit the right file.
# journalctl, briefly — the full guide is its own post
The essentials, in four commands. journalctl -b -p err — every error since this boot. journalctl -u <unit> --since "1 hour ago" — one unit, one window. journalctl -b -1 — the previous boot. journalctl -f — follow live, narrowly, with -u attached. Everything else is filtering by field (_SYSTEMD_UNIT=, _PID=, _COMM=), output format (-o json for scripts, -o cat for tickets), and storage (--disk-usage, --vacuum-size=, --verify). The full guide — the investigation order, field filtering, volatile vs persistent storage, and the rsyslog split — is journalctl Beyond -u and –since. This post does not repeat it.
# loginctl — sessions, seats, and the tmux that died at logout
What logind tracks. systemd-logind keeps three kinds of object: sessions (one login — an SSH connection, a console login, a graphical session), users (the accounts those sessions belong to), and seats (the hardware a session is attached to — a physical console, or none, for SSH). loginctl list-sessions shows them all with session ID, user, seat, and TTY; loginctl list-users and loginctl list-seats show the other two. loginctl show-session <id> prints the properties — state, type, class, remote or local, idle hint, locked hint — and loginctl session-status <id> adds the processes and the unit hierarchy under it. show-user and show-seat do the same for their objects.
Ending things, precisely. loginctl terminate-session <id> ends one session and its processes. loginctl terminate-user <user> ends all of a user’s sessions and their processes — the command for “this account is done on this box”. loginctl kill-user <user> and kill-session <id> send a signal instead of terminating, with --signal= to pick it. loginctl lock-session <id> and unlock-session <id> lock and unlock one session; lock-sessions locks them all. Locking is not terminating — the session and its processes stay, behind the lock screen.
Linger is the one everyone needs once a year. By default a user’s manager — and its user services — stop when the last session closes. loginctl enable-linger <user> keeps the user manager running after logout, so user services keep running and start at boot without a login. loginctl disable-linger <user> reverses it. Check it with loginctl show-user <user> | grep Linger, or list every lingering user with ls /var/lib/systemd/linger — a file per user, named for the user. Linger is also the correct fix for the tmux problem below, and the only fix that does not change the behaviour for every other user on the box.
The tmux problem, stated plainly. Since systemd 230, the upstream default is KillUserProcesses=yes in /etc/systemd/logind.conf: when the last session ends, logind kills the user’s remaining processes — including a tmux or screen server, and anything started with nohup. That is why a tmux session survives a disconnect on some boxes and dies on others: Debian and Ubuntu ship the setting as no, so processes survive there by default. The file on the box in front of you shows its own commented default — read it before you assume. The fixes, in order of preference: enable linger for that user; or, if the whole box is single-user, set KillUserProcesses=no in logind.conf and restart systemd-logind. Do not set it to no on a shared box to fix one user’s tmux — that trades one user’s convenience for every user’s processes surviving logout.
Idle handling is configuration, not a command. IdleAction= and IdleActionSec= in logind.conf decide what happens to an idle session — the action defaults to ignoring it, and the idle time defaults to 30 minutes. The actions are lock, suspend, hibernate, poweroff, reboot, and a few more; on a server the only one you will ever set is lock. loginctl show-session <id> shows the current idle hint, which is how you check whether the session is actually idle before you blame the configuration.
The tmux session that dies at logout is not a tmux bug. It is a logind default, and the fix is one command on the right user — not a global setting on a shared box.
# the identity trio — hostnamectl, timedatectl, localectl
hostnamectl is the hostname, and two other names. A host has three hostnames: the static one (from /etc/hostname, the one that survives a reboot), the transient one (from DHCP or the kernel, which can change), and the pretty one (a free-form display name, spaces allowed). hostnamectl with no arguments shows all three, plus the machine ID, the boot ID, the OS, the kernel, and the chassis. hostnamectl set-hostname <name> sets the static name; add --transient or --pretty to set the other two. hostnamectl set-chassis server sets the form factor — the values are desktop, laptop, server, tablet, vm, container, and a few more — and set-deployment and set-location record where the box is and what it is for. None of this is cosmetic trivia on a fleet: the machine ID and the chassis are what an inventory system keys off, and a box whose transient name came from DHCP is a box whose name changes when the lease does.
timedatectl is the clock, and the timezone. timedatectl with no arguments shows the local time, the universal time, the RTC time, the timezone, and whether NTP is on. timedatectl set-timezone America/Toronto sets the zone; timedatectl list-timezones lists them. timedatectl set-time "2026-11-11 14:00:00" sets the clock by hand — do it once, on a box with no NTP, and never as a habit. timedatectl set-ntp true turns NTP synchronization on; timedatectl timesync-status shows the server and the offset. The NTP daemon itself is the distro’s choice, not timedatectl’s: on RHEL it is chronyd, on Ubuntu it is systemd-timesyncd. timedatectl flips the switch; the daemon does the work. timedatectl set-local-rtc 1 makes the hardware clock local time instead of UTC — the dual-boot setting, and the wrong one on a server.
localectl is the locale and the keyboard. localectl with no arguments shows the locale, the console keymap, and the X11 keymap. localectl list-locales lists what is installed; localectl set-locale LANG=en_US.UTF-8 sets it — note the full assignment, not just the value. localectl list-keymaps and localectl set-keymap us handle the console; localectl set-x11-keymap us handles X11, and set-keymap --no-convert sets the console without touching X11. On a headless server the keymap rarely matters; the locale always does, because it decides the character set every tool on the box assumes.
# systemd-analyze — why the boot was slow, and whether the service is sandboxed
Boot time, three ways. systemd-analyze time (or bare systemd-analyze, which means the same thing) prints the kernel, initrd, and userspace phases and the total. systemd-analyze blame lists the units by how long they took to initialize, slowest first — and read it with the man page’s warning attached: a service that waits on another service looks slow, and a Type=simple service does not appear at all, because systemd considers it started immediately. systemd-analyze critical-chain prints the chain of units that actually gated the boot, with the time each one was reached and how long it took — the chain, not the list, is what tells you what to fix. systemd-analyze plot > boot.svg draws the whole boot as a timeline you can open in a browser, and systemd-analyze dot | dot -Tsvg > deps.svg draws the dependency graph instead.
The two commands that pay for the tool. systemd-analyze verify <unit-file> checks a unit file for errors before you install it — unknown keys, bad references, missing binaries — and it is the cheapest review a unit file ever gets. systemd-analyze security <unit> scores a service’s sandboxing on a scale of 0.0 to 10.0, where low is good: it checks every sandboxing directive systemd knows about and reports an exposure level per directive plus an overall figure, in bands from PERFECT down to DANGEROUS. The man page’s own example scores systemd-logind.service at 4.1, OK. Read the man page’s caveat with the number: it measures only the sandboxing systemd itself applies, not anything inside the service’s code, so a high exposure level means “this unit file has almost no sandboxing directives”, not “this service is vulnerable”. Treat it as a checklist to drive down, not a verdict — and use --offline=true to score a unit file without loading it, and --threshold= to fail a build when the score is above the line you set.
The small ones. systemd-analyze calendar "Mon *-*-* 03:00:00" tests whether an OnCalendar expression parses and when it next fires — the way to check a timer without waiting for Monday. systemd-analyze timestamp "yesterday" parses a timestamp the way the journal does. systemd-analyze condition ConditionPathExists=/etc/passwd evaluates a Condition directive outside a unit. systemd-analyze cat-config systemd/logind.conf shows a configuration file as systemd actually resolves it, drop-ins included. systemd-analyze exit-status 1 64 names exit codes; capability lists Linux capabilities by number; syscall-filter lists the syscall sets a filter name expands to. None of these are daily commands. All of them are the command you want when the daily ones stop answering.
blame tells you which unit was slow. critical-chain tells you which unit made the boot slow. They are not the same list.
# resolvectl and networkctl, briefly — the full guide is its own post
Two tools, one condition each: the service behind them has to be running. resolvectl talks to systemd-resolved: resolvectl status shows the DNS servers and domains per link, resolvectl query <name> resolves one name and shows which server answered, resolvectl flush-caches clears the cache, and resolvectl dns <link> <server> sets a per-link server. The old name was systemd-resolve, renamed in systemd 239; on RHEL 7 the old name is all there is. On Ubuntu, resolved is the default resolver, so resolvectl always has something to talk to. On RHEL, NetworkManager owns DNS by default and resolved is usually not running — systemctl status systemd-resolved first, or resolvectl has nothing to say. networkctl talks to systemd-networkd: networkctl status <link> shows addresses, routes, DNS, and NTP per interface, networkctl list shows the links, networkctl lldp shows the neighbours, and networkctl reload and reconfigure apply changed configuration. Same condition: on Ubuntu servers netplan drives networkd, so networkctl works; on RHEL, NetworkManager is the default and networkd is not running unless you started it. The full treatment of both — resolved, networkd, netplan, and why your DNS comes from the wrong place — is Ubuntu Networking, Untangled. This post does not repeat it.
# coredumpctl and busctl — the crash, and the bus underneath everything
coredumpctl is the crash log. When a process dies on a signal, systemd-coredump catches the core and stores it in /var/lib/systemd/coredump — on RHEL the kernel’s core pattern already points there by default. coredumpctl list shows the crashes, newest last unless you pass --reverse; coredumpctl list <pid> filters to one process. coredumpctl info <pid> shows the metadata — signal, executable, timestamp, and where the core is stored. coredumpctl dump <pid> -o /tmp/core extracts the core to a file; coredumpctl debug <pid> opens it in gdb (the verb used to be called gdb, renamed in systemd 239 — the old name still works). Two gotchas, both real. On early RHEL 9, services shipped with a soft core limit of 0, so no cores were written at all until a later update changed the default to unlimited — if coredumpctl list is empty on a box that definitely crashed, check DefaultLimitCORE in /etc/systemd/system.conf first. And on Ubuntu 24.04, systemd-coredump is a separate package (apt install systemd-coredump), and Apport can intercept crashes before they reach it.
busctl is D-Bus, from the terminal. Every *ctl tool above talks to systemd over D-Bus, and busctl is the tool that lets you do it by hand. busctl list shows the services on the bus; busctl status <name> shows one service’s details. busctl tree <name> shows the object tree; busctl introspect <name> <path> shows the interfaces and methods on one object. busctl call <name> <path> <interface> <method> calls a method; busctl get-property and set-property read and write properties. busctl monitor watches the traffic live. Add --user to talk to the session bus instead of the system bus. You will use busctl rarely. When you need it, there is no substitute: it is the difference between guessing what a service exposes and reading it.
# the rest of the bench — bootctl, and the tools that are not *ctl
bootctl is the bootloader tool — for systemd-boot. bootctl status shows the bootloader, the entries, and the default; bootctl list shows the boot entries; bootctl set-default <id> and set-oneshot <id> pick the next boot and the one after it; bootctl install and update manage the bootloader itself. The condition is the bootloader: on a systemd-boot box this is the tool; on a GRUB box — which is every RHEL and Ubuntu server by default — the equivalent is grubby and grub2-set-default, not bootctl. Knowing which bootloader you have is the whole of this section.
The rest are not *ctl, and mostly not daily. systemd-run starts a transient unit — a one-off command as a service, with properties attached (systemd-run --scope --user /usr/bin/screen is the lingering-safe way to start a multiplexer). systemd-mount and systemd-umount mount through the automount machinery. systemd-nspawn is the container tool. systemd-dissect inspects disk images. systemd-sysext, systemd-repart, systemd-stub, and the rest belong to image and UKI work, not to running a fleet. They share the name and the man-page family; they do not share this post.
# quick reference — the whole post in six lines
Services: systemctl status --failed, then enable --now, mask to forbid, edit for drop-ins, daemon-reload after — never edit /usr/lib/systemd/system/ by hand.
Logs: journalctl -b -p err, journalctl -u <unit> --since "1 hour ago" — the full guide is journalctl Beyond -u and –since.
Sessions: loginctl list-sessions, terminate-user to end them, enable-linger to keep a user’s services past logout.
Identity: hostnamectl set-hostname, timedatectl set-timezone, localectl set-locale LANG= — three tools, three files you never edit by hand again.
Why slow: systemd-analyze blame for the list, critical-chain for the cause, security for the sandboxing score.
Crashes: coredumpctl list, info, debug — and check DefaultLimitCORE first if the list is empty.

Commands verified against systemd documentation, November 2026: systemd.io and the man pages systemctl(1), loginctl(1), journalctl(1), hostnamectl(1), timedatectl(1), localectl(1), systemd-analyze(1), resolvectl(1), networkctl(1), coredumpctl(1), busctl(1), bootctl(1), and logind.conf(5); Oracle’s Use Systemd on Oracle Linux lab for linger and logind.conf; Red Hat’s RHEL 9 debugging documentation for coredumpctl and the DefaultLimitCORE change. systemd versions: RHEL 7 = 219, RHEL 8 = 239, RHEL 9 = 252, RHEL 10 = 257; Ubuntu 22.04 = 249, 24.04 = 255, 26.04 = 259. The boot sequence these tools manage is in Linux Boot Sequence: From Power-On to Login Prompt.

Series: GuidesStatus: Published 12 Nov 2026

Scroll to Top