Skip to content
Français
Account: not signed in, sync available My learning
Select theme
Français

Linux reboot and shutdown: shutdown, systemctl

By updated

Improve this page

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, poweroff and systemctl depending 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) from set-default (at the next boot)
  • Switch to rescue mode without losing access to a remote server
  • Make the journal persistent so that journalctl -b -1 helps 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 sudo or root rights
  • 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.

RunlevelRole
0Full stop (shutdown)
1Single user (maintenance)
2Multi-user without network (variable)
3Multi-user with network (console)
5Multi-user with graphical interface
6Reboot

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.

Terminal window
ls -l /usr/lib/systemd/system/runlevel*.target

The 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.target
runlevel1.target -> rescue.target
runlevel2.target -> multi-user.target
runlevel3.target -> multi-user.target
runlevel4.target -> multi-user.target
runlevel5.target -> graphical.target
runlevel6.target -> reboot.target

Seven 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:

Terminal window
systemctl get-default
multi-user.target
Terminal window
sudo systemctl set-default multi-user.target
Removed '/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.

Terminal window
sudo systemctl isolate multi-user.target # stops the graphical interface

After the switch, runlevel shows two values, the previous level then the current level, which lets you see the change:

5 3

Key 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:

NeedCommandImmediate effectSurvives reboot
Change the state nowsystemctl isolate <target>yesno
Change the state at the next bootsystemctl set-default <target>noyes
Bothboth commandsyesyes

Not every target can be isolated. The AllowIsolate property says so, one target at a time:

Terminal window
systemctl show -p AllowIsolate --value graphical.target

The 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.

Terminal window
# immediate shutdown
sudo shutdown -h now
# shutdown in 10 minutes
sudo shutdown -h +10
# scheduled shutdown with a message to users
sudo shutdown -h +5 "Maintenance: kernel patch"
# shutdown at a precise time (24h format)
sudo shutdown -h 23:00

To cancel a scheduled shutdown as long as it has not started, one option is enough, -c:

Terminal window
sudo shutdown -c

poweroff, stop and cut the power

poweroff takes no argument and acts immediately:

Terminal window
sudo poweroff

Equivalent 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:

Terminal window
sudo halt

halt 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:

Terminal window
sudo systemctl poweroff

The 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:

Terminal window
sudo reboot

Works 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:

Terminal window
sudo shutdown -r now # immediate reboot
sudo shutdown -r +5 "update" # in 5 minutes, with a message
sudo shutdown -c # cancel

The 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:

Terminal window
sudo systemctl reboot

The 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 5
get-default : graphical.target
is-system-running : running
failed units : 0

No error, no failed unit, the system reports running. The cause lies in a single line of the target definition:

Terminal window
systemctl cat graphical.target
[Unit]
Description=Graphical Interface
Documentation=man:systemd.special(7)
Requires=multi-user.target
Wants=display-manager.service
Conflicts=rescue.service rescue.target
After=multi-user.target rescue.service rescue.target display-manager.service
AllowIsolate=yes

Identical 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:

Terminal window
systemctl get-default # must answer multi-user.target on a server

Rescue 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:

Terminal window
systemctl cat rescue.target
[Unit]
Description=Rescue Mode
Documentation=man:systemd.special(7)
Requires=sysinit.target rescue.service
After=sysinit.target rescue.service
AllowIsolate=yes

Neither 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:

Terminal window
systemctl list-dependencies rescue.target | grep -cE 'sshd|network-online'
0

The service the target starts completes the demonstration:

Terminal window
systemctl cat rescue.service | grep -E 'ExecStart|StandardInput'
ExecStart=-/usr/lib/systemd/systemd-sulogin-shell rescue
StandardInput=tty-force

StandardInput=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:

Terminal window
sudo systemctl isolate rescue.target

All 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.target

Then 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.target

The root filesystem is then mounted read-only. To switch it to RW in order to repair:

Terminal window
mount -o remount,rw /

Classic case: reset a lost root password

  1. Reboot and enter GRUB, interrupt the automatic boot.

  2. Edit the linux line and add at the end of the line the parameter matching your distribution family. The two are not interchangeable: rd.break is a dracut parameter, the initramfs of RHEL, and Debian does not answer it.

    On RHEL, Rocky, AlmaLinux and Fedora:

    rd.break

    On Debian and Ubuntu, which use initramfs-tools:

    init=/bin/bash
  3. Boot (Ctrl+X). On RHEL, the system stops before mounting / and you get a switch_root:/# shell in the initramfs. On Debian, you directly get a root shell, with the root mounted read-only.

  4. Mount the root read-write. In both cases, it is mounted read-only: without this step, passwd will fail.

    On RHEL and derivatives, the real root is under /sysroot, so you must lock yourself into it with chroot:

    Terminal window
    mount -o remount,rw /sysroot
    chroot /sysroot

    On Debian and Ubuntu, you already are on the real root:

    Terminal window
    mount -o remount,rw /
  5. Change the password:

    Terminal window
    passwd root
  6. Force the SELinux relabelling (RHEL, Rocky, Fedora only). Without it, the new /etc/shadow carries a wrong security context and the system refuses the login at reboot:

    Terminal window
    touch /.autorelabel
  7. Exit and reboot. On RHEL:

    Terminal window
    exit # leaves the chroot
    exit # leaves the initramfs, the boot goes on

    On Debian and Ubuntu, init=/bin/bash replaced 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:

Terminal window
journalctl -b -1

Yet 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:

Terminal window
sudo mkdir -p /var/log/journal
sudo systemd-tmpfiles --create --prefix /var/log/journal
sudo systemctl restart systemd-journald

Check that the journal did move to disk, with two quick commands:

Terminal window
ls -d /var/log/journal/*
journalctl --disk-usage

The 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:

MessageCauseAction
no persistent journal was found/var/log/journal/ missing, journal in memoryCreate the directory, restart journald
No journal boot entry found for the specified boot (-1)Persistent journal, but a single boot recordedWait 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 UTC

The -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 +N to reboot as soon as other users may be logged in.
  • Announce a maintenance with an explicit message in the command (-k sends a wall message without shutting down, useful to warn before actually rebooting).
  • Check systemctl list-units --failed before 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 -1 will be useless on the day of the incident.
  • last reboot and last shutdown list 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 -c as long as the shutdown has not begun.
  • Boot once into an alternative target: in the GRUB menu, edit the linux line (key e) and add systemd.unit=<target>, which only applies to that boot. The systemctl reboot --boot-loader-menu=10 option asks the boot loader to show its menu at the next boot, but the systemctl manual 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.

SymptomLikely causeAction
reboot returns but the machine does not rebootService stuck at stop, waiting for its timeoutWait or force: sudo systemctl reboot --force --force
The system boots into rescue mode for no reasonBroken /etc/fstab or missing deviceEdit /etc/fstab, systemctl daemon-reload, then reboot
shutdown -c cancels nothingThe shutdown has already started its final phaseToo late, wait for the reboot
The root password does not work after passwd rootSELinux relabel not triggeredAt the next boot: touch /.autorelabel before leaving
No console after booting into rescueThe target requires a root password you do not haveUse rd.break at the initramfs level
The system reboots in a loop after rebootBroken kernel or missing driverGRUB → choose the previous kernel ("advanced options" entry)
systemctl list-units --failed lists several services after rebootBadly ordered dependencies or missing resourcejournalctl -xe -u <service> for each failed service
The machine is unreachable after systemctl isolate rescue.targetExpected: the target carries neither network nor sshdGo 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 memorymkdir -p /var/log/journal then systemctl restart systemd-journald
journalctl -b -1 answers No journal boot entry foundPersistent journal, but a single boot recordedCheck with journalctl --list-boots, nothing to fix
systemctl isolate basic.target refusedAllowIsolate=no on intermediate targetsIsolate a complete-state target (multi-user, graphical, rescue)
A headless server boots in runlevel 5Default target left on graphical.targetsystemctl 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). halt stops 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 -c cancels, 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-default handles the next boot.
  • systemctl isolate rescue.target cuts remote access: the target carries neither network nor sshd, and its shell is forced onto the console. From SSH, go through systemd.unit=rescue.target in GRUB.
  • rd.break in GRUB lets you reset a lost root password (with an SELinux relabel if applicable).
  • journalctl -b -1 requires 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.service is a weak dependency. Only systemctl get-default reveals 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

6 questions
6 min
70% to pass

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

Is this project useful to you?

The guides, courses, quizzes and labs stay free. If you use them regularly, you can contribute to their upkeep and development.

Become a supporting member (new tab) One-time support (new tab) Learn more

Follow the project on LinkedIn