Shutting down or rebooting a Linux system looks trivial, until the day a blunt reboot cuts a database transaction, an rsync session, or your own SSH connection with no way back. Modern systems expose several commands (shutdown, reboot, poweroff, systemctl) with close but not identical behaviours, and systemd integrates them all into a model of targets that replaces the old runlevels. This guide sets the minimum to know to choose the right command, schedule a maintenance cleanly, and never get stuck in degraded mode.
What you will learn
- Choose between
shutdown,reboot,poweroffandsystemctldepending on the context - Schedule a shutdown or a reboot while warning the active sessions
- Cancel a scheduled shutdown before it starts
- Understand the SysV runlevels ↔ systemd targets mapping, and why it only goes one way
- Tell apart
isolate(now) fromset-default(at the next boot) - Switch to rescue mode without losing access to a remote server
- Make the journal persistent so that
journalctl -b -1helps on the day of the incident - Reset a lost root password through GRUB
In which context?
Three concrete scenarios justify mastering these commands, from the planned to the urgent:
- Scheduled maintenance, you must reboot after installing a kernel or a major update. Warn the users, give services time to finish cleanly, cancel if someone objects.
- Urgent intervention, a service is stuck, commands no longer answer, you must reboot without breaking the filesystem.
- Recovery, lost root password, broken
/etc/fstab, system that does not mount/. Go through rescue mode rather than a live CD whenever possible.
The RHCSA (EX200, RHEL 10) devotes three objectives to it under Operate running systems: "Boot, reboot, and shut down a system normally", "Boot systems into different targets manually" and "Interrupt the boot process in order to gain access to a system". The LFCS covers it more broadly, with "Recover from hardware, operating system, or filesystem failures" in the Operations Deployment domain.
Prerequisites
- A shell with
sudoorrootrights - A modern Linux server (systemd: Ubuntu 16.04+, Debian 8+, RHEL 7+, Rocky, Fedora)
- For the rescue/GRUB operations: physical or virtual console access, SSH is not enough when the system is degraded
Runlevels, targets: the model
The SysVinit legacy: runlevels
Runlevels are numeric execution levels inherited from Unix System V. You will still meet them in documentation and on some legacy systems.
| Runlevel | Role |
|---|---|
0 | Full stop (shutdown) |
1 | Single user (maintenance) |
2 | Multi-user without network (variable) |
3 | Multi-user with network (console) |
5 | Multi-user with graphical interface |
6 | Reboot |
The default runlevel was described in /etc/inittab and each level had its scripts under /etc/rc<N>.d/ (S* to start, K* to stop a service).
The modern model: systemd targets
With systemd, runlevels are replaced by more expressive and composable targets. Each target groups units (services, sockets, mounts, timers) and can depend on other targets.
This mapping is not a conversion table made up by the documentation: it is written into the system, as symbolic links you can read yourself.
ls -l /usr/lib/systemd/system/runlevel*.targetThe path is the same on the three reference distributions: on Debian and Ubuntu, /lib is now only a link to /usr/lib (checked).
runlevel0.target -> poweroff.targetrunlevel1.target -> rescue.targetrunlevel2.target -> multi-user.targetrunlevel3.target -> multi-user.targetrunlevel4.target -> multi-user.targetrunlevel5.target -> graphical.targetrunlevel6.target -> reboot.targetSeven links, but only five distinct targets: runlevels 2, 3 and 4
all point to multi-user.target. This one-way mapping
is the important lesson. Under SysVinit, moving from runlevel 2 to runlevel 3
really changed the state of the machine; under systemd, it is strictly the
same target. The runlevel is no longer a mechanism, it is a compatibility
layer kept so that old scripts and old habits
keep working. Practical corollary: init 3 does not "go down"
from a graphical session to a console mode different from init 2, both
do exactly the same thing.
Read and change the default target
The default target is the one systemd reaches automatically at each boot. Two commands are enough to read and write it, and it is important to understand that they only handle a symbolic link:
systemctl get-defaultmulti-user.targetsudo systemctl set-default multi-user.targetRemoved '/etc/systemd/system/default.target'.Created symlink '/etc/systemd/system/default.target' → '/usr/lib/systemd/system/multi-user.target'.The output reveals the whole mechanism: default.target in
/etc/systemd/system/ is a link to the wanted target. You can
also check the current state without going through systemctl, with a plain
ls -l /etc/systemd/system/default.target.
isolate, switch without rebooting
systemctl isolate starts a target and stops everything that does not depend on it.
It is this second effect, often left unsaid, that makes it a command to
handle with care.
sudo systemctl isolate multi-user.target # stops the graphical interfaceAfter the switch, runlevel shows two values, the previous level then the
current level, which lets you see the change:
5 3Key point: isolate does not change the default target. A
systemctl get-default right after the switch still returns the old
value, and the next reboot will start from it again. The two commands
answer two distinct needs, which this table separates:
| Need | Command | Immediate effect | Survives reboot |
|---|---|---|---|
| Change the state now | systemctl isolate <target> | yes | no |
| Change the state at the next boot | systemctl set-default <target> | no | yes |
| Both | both commands | yes | yes |
Not every target can be isolated. The AllowIsolate property says so, one target at a time:
systemctl show -p AllowIsolate --value graphical.targetThe targets that represent a complete system state (multi-user,
graphical, rescue, emergency) answer yes. Targets that are only
intermediate milestones (basic, sysinit, network-online) answer
no: isolating them would leave the machine in an inconsistent state, so systemd refuses
the command.
Shut down a machine
Several commands, a single goal: cut cleanly, without losing data.
shutdown, the canonical method
shutdown warns the logged-in users and gives services time to finish. It is the command to prefer in production.
# immediate shutdownsudo shutdown -h now
# shutdown in 10 minutessudo shutdown -h +10
# scheduled shutdown with a message to userssudo shutdown -h +5 "Maintenance: kernel patch"
# shutdown at a precise time (24h format)sudo shutdown -h 23:00To cancel a scheduled shutdown as long as it has not started, one option is enough, -c:
sudo shutdown -cpoweroff, stop and cut the power
poweroff takes no argument and acts immediately:
sudo poweroffEquivalent to shutdown -h now + an explicit power cut. Useful on a physical server to be sure the machine is really off.
halt, stop without cutting the power
halt looks like poweroff, but its result differs:
sudo halthalt stops the system without switching the machine off: the systemctl manual (systemd 257) states that the operation halts the kernel while leaving the hardware powered on, and points to poweroff to cut the power. On a remote server, a halt therefore leaves a machine powered on but inert, which will have to be restarted through the console.
systemctl poweroff
The systemd form of the same command, run with sudo like the others:
sudo systemctl poweroffThe systemd form, equivalent to poweroff on a modern system. Worth knowing for consistency with the rest of the systemd ecosystem. It can also schedule: the systemd 257 manual documents --when=, which takes a timestamp, and --when=cancel to cancel. Unlike shutdown +N "message", nothing there carries a custom message to the sessions.
init 0 / telinit 0
Legacy commands still supported through SysV compatibility. Worth knowing to understand old scripts, not for daily use on a systemd system.
Reboot a machine
The same tools, with different options, and the same caution in production.
reboot, quick and universal
reboot needs no option and works everywhere:
sudo rebootWorks on every distribution. Ends the processes, unmounts the filesystems, then reboots.
shutdown -r, scheduling
shutdown -r accepts the same delays and messages as shutdown -h:
sudo shutdown -r now # immediate rebootsudo shutdown -r +5 "update" # in 5 minutes, with a messagesudo shutdown -c # cancelThe version to prefer when other users may be logged in, because it warns them.
systemctl reboot
The systemd form of reboot, with the same effect:
sudo systemctl rebootThe systemd form, equivalent to reboot. Consistent with systemctl poweroff, systemctl rescue, and so on.
init 6 / telinit 6
Legacy SysV, same function as reboot, kept for old scripts.
A graphical target on a server goes unnoticed
A badly set default target is a silent defect, which
explains why it lingers for months on production machines. On a server
without a graphical interface whose default target is graphical.target, the
boot goes... normally:
runlevel : N 5get-default : graphical.targetis-system-running : runningfailed units : 0No error, no failed unit, the system reports running. The cause
lies in a single line of the target definition:
systemctl cat graphical.target[Unit]Description=Graphical InterfaceDocumentation=man:systemd.special(7)Requires=multi-user.targetWants=display-manager.serviceConflicts=rescue.service rescue.targetAfter=multi-user.target rescue.service rescue.target display-manager.serviceAllowIsolate=yesIdentical content on Debian 13, Ubuntu 24.04 and AlmaLinux 10 (read in /usr/lib/systemd/system/graphical.target).
graphical.target wants (Wants=) the session manager, it does not
require it (Requires=). Yet Wants= is a weak dependency: if the
unit is missing or fails, the target is still considered
reached. On a machine where no graphical package is installed,
display-manager.service simply does not exist, and nobody is
warned.
It is not a theoretical case: Ubuntu Server 24.04 ships with
graphical.target as its default target, although the installation
contains neither gdm3 nor ubuntu-desktop. The machine boots in runlevel 5 without
a single interface package.
The real impact is low on a minimal server image, since nothing graphical is there to be started. It becomes concrete as soon as packages pull a graphical dependency by accident: the server then starts a full stack nobody will ever look at. The check fits in one command, to add to compliance checks:
systemctl get-default # must answer multi-user.target on a serverRescue and maintenance mode
The rescue target switches the system to a minimal state: root
filesystem mounted, root shell open on the console, no network and no
services. Useful to repair /etc/fstab, change a password, run
fsck.
Switch to rescue from a running system
The reason is not an opinion, it is written in the unit definitions.
rescue.target only pulls two dependencies:
systemctl cat rescue.target[Unit]Description=Rescue ModeDocumentation=man:systemd.special(7)Requires=sysinit.target rescue.serviceAfter=sysinit.target rescue.serviceAllowIsolate=yesNeither network.target, nor sshd.service, nor anything that pulls them
indirectly. You can confirm it on your own machine without breaking anything,
the command only reads:
systemctl list-dependencies rescue.target | grep -cE 'sshd|network-online'0The service the target starts completes the demonstration:
systemctl cat rescue.service | grep -E 'ExecStart|StandardInput'ExecStart=-/usr/lib/systemd/systemd-sulogin-shell rescueStandardInput=tty-forceStandardInput=tty-force forces the shell onto the physical
console. The rescue shell will therefore not appear in your SSH session,
which will have vanished anyway with the stop of sshd.
The command remains perfectly legitimate, provided you sit in front of the right console:
sudo systemctl isolate rescue.targetAll non-essential services are stopped, a root shell waits on the console. On a remote machine, always prefer entering rescue at boot described just after: it at least lets you come back with a simple reboot.
Enter rescue at boot
Through the GRUB menu, edit the linux line (key e) and add at the end:
systemd.unit=rescue.targetThen Ctrl+X to boot with this option (one time only). Very useful when the system does not finish its normal boot.
emergency mode, even lower
If even rescue does not start (broken filesystem, for example), switch to emergency.target, which does not even try to mount the filesystem in read-write:
systemd.unit=emergency.targetThe root filesystem is then mounted read-only. To switch it to RW in order to repair:
mount -o remount,rw /Classic case: reset a lost root password
-
Reboot and enter GRUB, interrupt the automatic boot.
-
Edit the
linuxline and add at the end of the line the parameter matching your distribution family. The two are not interchangeable:rd.breakis a dracut parameter, the initramfs of RHEL, and Debian does not answer it.On RHEL, Rocky, AlmaLinux and Fedora:
rd.breakOn Debian and Ubuntu, which use
initramfs-tools:init=/bin/bash -
Boot (
Ctrl+X). On RHEL, the system stops before mounting/and you get aswitch_root:/#shell in the initramfs. On Debian, you directly get a root shell, with the root mounted read-only. -
Mount the root read-write. In both cases, it is mounted read-only: without this step,
passwdwill fail.On RHEL and derivatives, the real root is under
/sysroot, so you must lock yourself into it withchroot:Terminal window mount -o remount,rw /sysrootchroot /sysrootOn Debian and Ubuntu, you already are on the real root:
Terminal window mount -o remount,rw / -
Change the password:
Terminal window passwd root -
Force the SELinux relabelling (RHEL, Rocky, Fedora only). Without it, the new
/etc/shadowcarries a wrong security context and the system refuses the login at reboot:Terminal window touch /.autorelabel -
Exit and reboot. On RHEL:
Terminal window exit # leaves the chrootexit # leaves the initramfs, the boot goes onOn Debian and Ubuntu,
init=/bin/bashreplaced the init process: there is no shell to leave. You hand back to the normal boot by starting systemd in place of the shell:Terminal window exec /sbin/init # hands back to the normal boot
Analyse the previous boot
After an incident, the natural question is "what happened at the previous boot?". The command exists:
journalctl -b -1Yet it fails on many freshly installed server images, with a message that at least has the merit of being explicit:
Specifying boot ID or boot offset has no effect, no persistent journal was found.The cause is that journald then writes to /run/log/journal/,
in memory. The Storage=auto setting means "persistent if the
/var/log/journal/ directory exists, volatile otherwise", and the minimal images of RHEL,
AlmaLinux or Rocky do not create it: the systemd package of AlmaLinux 10 declares
the path without creating the directory, whereas that of Debian 13 and Ubuntu
24.04 creates it at installation (read), which makes their journal persistent
by default. The whole journal therefore disappears at each reboot on RHEL, precisely when it becomes interesting.
Make the journal persistent
Creating the directory is enough, journald switches when the service restarts:
sudo mkdir -p /var/log/journalsudo systemd-tmpfiles --create --prefix /var/log/journalsudo systemctl restart systemd-journaldCheck that the journal did move to disk, with two quick commands:
ls -d /var/log/journal/*journalctl --disk-usageThe first-reboot trap
Enabling persistence does not retroactively recover the previous boot: the logs from before were in memory, they are lost. Right after the first reboot, the command therefore still fails, but the message is no longer the same:
No journal boot entry found for the specified boot (-1).This nuance is worth knowing, because the two messages point to two different diagnoses:
| Message | Cause | Action |
|---|---|---|
no persistent journal was found | /var/log/journal/ missing, journal in memory | Create the directory, restart journald |
No journal boot entry found for the specified boot (-1) | Persistent journal, but a single boot recorded | Wait for the next reboot, nothing to fix |
It therefore takes two reboots after enabling it for journalctl -b -1
to finally return something. journalctl --list-boots is the command that removes
any doubt, by showing exactly the available boots:
IDX BOOT ID FIRST ENTRY LAST ENTRY -1 e7f529b9f02643ad8e7407a4b124885d Thu 2026-07-23 10:25:55 UTC Thu 2026-07-23 10:26:20 UTC 0 7b5132f8b50b41b18ed98256bb8d9eeb Thu 2026-07-23 10:26:23 UTC Thu 2026-07-23 10:26:40 UTCThe -1 index is the one -b -1 designates. If it does not appear in this
list, no option of journalctl will make it appear.
Best practices
These reflexes separate a controlled reboot from a roll of the dice. The common thread
is the same everywhere: warn before acting and check the state of the system
before and after. A reboot has never repaired a service already failed, so
you must look at systemctl list-units --failed beforehand, not hope the
reboot will clean things up.
- Prefer
shutdown -r +Ntorebootas soon as other users may be logged in. - Announce a maintenance with an explicit message in the command (
-ksends a wall message without shutting down, useful to warn before actually rebooting). - Check
systemctl list-units --failedbefore a reboot: a service already failed will not magically restart after the reboot, it must be diagnosed first. - Disable cleanly the services that must not restart (
systemctl disable) rather than killing them by hand at each boot. - Make the journal persistent from installation, otherwise
journalctl -b -1will be useless on the day of the incident. last rebootandlast shutdownlist the history from/var/log/wtmp, independently of journald.
Security and rollback
Any shutdown or target switch command must have its way out planned before running it, especially remotely. The following points answer a single question: what to do if the operation goes wrong? The most important is not in a command but in a principle, always keep an access that does not depend on the system being handled, physical console, IPMI or cloud console.
- Cancel a shutdown:
sudo shutdown -cas long as the shutdown has not begun. - Boot once into an alternative target: in the GRUB menu, edit the
linuxline (keye) and addsystemd.unit=<target>, which only applies to that boot. Thesystemctl reboot --boot-loader-menu=10option asks the boot loader to show its menu at the next boot, but thesystemctlmanual warns that not all boot loaders support it: check it on your machine before relying on it remotely. - Protect GRUB with a password to prevent anyone with console access from entering rescue, see Harden the bootloader (in French) in the BP-028 guide.
- Keep a physical / IPMI / cloud console access: if an updated kernel does not boot, it is the only way back.
Quick troubleshooting
This table gathers the most common incidents around shutdown,
reboot and systemd targets. Read it starting from the symptom observed,
the middle column names the cause to confirm before applying the action. Two
lines are not failures but expected behaviours mistaken
for bugs: the machine unreachable after isolate rescue.target, and
the No journal boot entry found message on a journal that has a single
recorded boot.
| Symptom | Likely cause | Action |
|---|---|---|
reboot returns but the machine does not reboot | Service stuck at stop, waiting for its timeout | Wait or force: sudo systemctl reboot --force --force |
| The system boots into rescue mode for no reason | Broken /etc/fstab or missing device | Edit /etc/fstab, systemctl daemon-reload, then reboot |
shutdown -c cancels nothing | The shutdown has already started its final phase | Too late, wait for the reboot |
The root password does not work after passwd root | SELinux relabel not triggered | At the next boot: touch /.autorelabel before leaving |
| No console after booting into rescue | The target requires a root password you do not have | Use rd.break at the initramfs level |
| The system reboots in a loop after reboot | Broken kernel or missing driver | GRUB → choose the previous kernel ("advanced options" entry) |
systemctl list-units --failed lists several services after reboot | Badly ordered dependencies or missing resource | journalctl -xe -u <service> for each failed service |
The machine is unreachable after systemctl isolate rescue.target | Expected: the target carries neither network nor sshd | Go through the physical console, IPMI or cloud console, then systemctl isolate multi-user.target |
journalctl -b -1 answers no persistent journal was found | /var/log/journal/ does not exist, journald writes to memory | mkdir -p /var/log/journal then systemctl restart systemd-journald |
journalctl -b -1 answers No journal boot entry found | Persistent journal, but a single boot recorded | Check with journalctl --list-boots, nothing to fix |
systemctl isolate basic.target refused | AllowIsolate=no on intermediate targets | Isolate a complete-state target (multi-user, graphical, rescue) |
| A headless server boots in runlevel 5 | Default target left on graphical.target | systemctl set-default multi-user.target |
Put it into practice
Changing the default target is the kind of operation you do not want to discover on a production server. This lab gives you a disposable VM to read the current target with systemctl get-default, switch to multi-user.target with set-default and confirm after a reboot that the system does boot on it.
Key points
- Four main commands:
shutdown(scheduled),reboot(quick),poweroff(switch off),systemctl poweroff/reboot(systemd form).haltstops the system but leaves the machine powered on. shutdown -r +N "message"is the default command in production, it warns users and gives services time.shutdown -ccancels, as long as the shutdown has not started.- systemd targets replace runlevels:
multi-user.target,graphical.target,rescue.target,emergency.target. Runlevels 2, 3 and 4 all designate the same target, they are only aliases now. systemctl isolate <target>switches on the fly without a reboot, but does not change the default target:set-defaulthandles the next boot.systemctl isolate rescue.targetcuts remote access: the target carries neither network norsshd, and its shell is forced onto the console. From SSH, go throughsystemd.unit=rescue.targetin GRUB.rd.breakin GRUB lets you reset a lost root password (with an SELinux relabel if applicable).journalctl -b -1requires a persistent journal: persistent from the start on Debian and Ubuntu, to enable on RHEL by creating/var/log/journal/, then count two reboots before the previous boot becomes readable.- A graphical target on a server causes no error:
Wants=display-manager.serviceis a weak dependency. Onlysystemctl get-defaultreveals the setting. - Protect GRUB with a password in production to prevent unauthorised rescue access.
Knowledge check
Check that the essentials of this guide stuck. The questions cover only what has just been explained here.
Knowledge check
Check what you have learned with this interactive quiz
How it works
- The timer starts when you click Start
- Multiple choice, true/false and short answer questions
- You can move between questions
- Detailed results are shown at the end
Starts the quiz and the timer
Review
(0/0)Score by domain
A domain groups several guides and gets several questions, so its score is more reliable than a per-topic score, which often rests on a single question.
Summary
Strengths
No topic passed at 80% on at least two questions yet.
Weak areas
No topic below the pass mark.
Recommended next
Hints before you try again?
Continue the course: Pass parameters to the kernel at boot
A new full quiz with random questions
Work again only on the questions you missed