Check a systemd Host's Runlevel Without Changing State
You will finish with a quick, read-only check of the compatibility runlevel on a systemd host, plus a way to see the active targets that the single number hides. The examples use runlevel from systemd 255, installed here as package systemd-sysv 255.4-1ubuntu8.17.
The route
Jump straight to the step you need, or tick off Done means at the end.
Allow about ten minutes. You need a shell on a running systemd machine. The basic checks need no elevated privileges and do not start, stop or reload anything. Do not use a runlevel query as a reason to isolate a target or reboot a host.
1. Confirm the installed command
Start by checking which executable the shell will run and which package supplied it:
$ command -v runlevel
/usr/sbin/runlevel
$ ls -l /usr/sbin/runlevel
lrwxrwxrwx ... /usr/sbin/runlevel -> ../bin/systemctl
$ dpkg-query -W -f='${Package} ${Version}\n' systemd-sysv
systemd-sysv 255.4-1ubuntu8.17
The exact permissions and date in the ls output will differ. On this installation, runlevel is a compatibility entry point into systemctl, rather than a separate SysV init implementation.
Checkpoint: if command -v finds a local script or another package, stop and read that command's own documentation. The output and compatibility details below are for the systemd implementation.
2. Read the previous and current values
Run the command on its own:
$ runlevel
N 5
$ printf 'exit status: %s\n' "$?"
exit status: 0
The two fields are previous and current, separated by one space. N means that a value could not be determined. It does not mean that the host is in a special runlevel called N. If neither value can be determined, the command prints unknown and returns a non-zero status. A successful status means that at least one value was found, not that the number is a complete description of the running system.
The normal lookup reads recent runlevel changes from /run/utmp. A newly booted host commonly has no useful previous value, so output such as N 5 is ordinary. Do not treat the first field as a failed check merely because it is N.
3. Translate the number into a systemd target
Runlevel names are an approximate compatibility layer. The local manual documents this mapping:
| Runlevel | systemd target |
|---|---|
| 0 | poweroff.target |
| 1 | rescue.target |
| 2, 3, 4 | multi-user.target |
| 5 | graphical.target |
| 6 | reboot.target |
This is not a one-to-one model of systemd. Systemd can have several targets active at once, while the compatibility command prints only one current runlevel. On a graphical machine, inspect the active targets directly:
$ systemctl list-units --type=target --state=active --no-legend
basic.target loaded active active Basic System
graphical.target loaded active active Graphical Interface
multi-user.target loaded active active Multi-User System
...
Your list will be longer and host-specific. The useful check is that the target you care about appears with the active state. Seeing both graphical.target and multi-user.target is normal because targets can be related and active together.
Checkpoint: use runlevel when an older script or operator procedure asks for the traditional number. Use the active-target list when diagnosing what systemd is actually managing.
4. Check a boot or service diagnosis safely
For a read-only diagnosis, query a target's state rather than trying to change it:
$ systemctl is-active graphical.target
active
$ systemctl is-active multi-user.target
active
systemctl is-active returns a status that scripts can test. The text can be active, inactive, failed or another state, depending on the unit. The command itself does not activate the target.
If you need to know why a target is active, inspect its status without changing it:
$ systemctl status graphical.target --no-pager
● graphical.target - Graphical Interface
Loaded: loaded (...)
Active: active
Some status details are omitted or formatted differently across systemd releases. Do not copy a unit path or timestamp from this example as a fixed value.
5. Understand environment overrides
The runlevel(8) manual documents RUNLEVEL as an override for the current value and PREVLEVEL as an override for the previous value. They are compatibility inputs, not persistent settings. Setting them in one command does not change systemd, /run/utmp or the next boot.
$ env RUNLEVEL=5 PREVLEVEL=3 runlevel
N 5
On the installed system used for this guide, the direct test still produced N 5, so do not assume that an override has been accepted merely because it was present in the shell environment. If a script depends on these variables, test the exact systemd build and invocation it will use, then check the returned fields. Prefer a direct systemd query for new code instead of building new automation around this legacy interface.
To inspect the environment without changing anything, use:
$ env | grep -E '^(RUNLEVEL|PREVLEVEL)='
No output means neither variable is exported in the current shell. Do not export guessed values to make a monitoring check look healthy.
6. Avoid the dangerous interpretation
runlevel reports state; it does not change state. The commands that isolate a target, power off a machine or reboot it are materially different and can stop services or end your session. They require a clear operational reason, a maintenance window where appropriate, and a recovery path. This guide intentionally does not provide a state-changing command.
Do not use sudo for the checks above. If a wrapper reports a permission error while reading status, investigate the wrapper, host policy and unit access rules first. Adding elevated privileges can make a diagnostic appear to work while hiding the account's real access.
Done means
- You confirmed which systemd package and executable provide
runlevel. - You can read the previous and current fields and interpret
Ncorrectly. - You can map the compatibility number to its approximate systemd target.
- You checked active targets when one runlevel number was too imprecise.
- You verified target state without using elevated privileges or changing services.
- You treated environment overrides as compatibility inputs and tested them on the installed build.