Booting a Linux machine is a relay race between programs that barely know each other exists. The firmware knows nothing about Linux. The bootloader knows nothing about your filesystem layout. The kernel starts with no drivers for the disk it is about to mount. Each stage does one job, hands control to the next, and then ceases to exist — which is precisely why boot failures are so hard to diagnose. The thing that broke is usually gone by the time you can look at anything.

This page follows one boot the whole way through, in order, and gives you the command that inspects each handoff. The single most useful skill it teaches is not any individual command: it is learning to work out how far the boot got, because that immediately eliminates most of the chain.

How Linux Boots covers the same sequence at the level you need to fix a normal problem. This is the level below it — and quite a lot of it changed in 2025 and 2026, which is the other reason to read it.

The handoffs, once, so the rest of the page makes sense. Eight of them, and each one is a point where a boot can stop:

  1. Firmware picks a boot entry and runs a file off the EFI System Partition.
  2. shim verifies the next stage against Secure Boot, and gets out of the way.
  3. The bootloader finds a kernel and an initramfs, and builds a command line.
  4. The kernel unpacks itself, brings up the CPU and memory, and unpacks the initramfs into a filesystem in RAM.
  5. Early userspace/init in that RAM filesystem — loads the drivers needed to reach the real root, and mounts it.
  6. The switch to the real root, after which the entire initramfs is deleted.
  7. systemd starts as PID 1 and pulls in everything that default.target depends on.
  8. getty or a display manager opens a terminal and asks who you are.

The single most useful diagnostic fact on this page: if journalctl -b -1 shows you anything at all from the failed boot, stages one to six succeeded. The journal only exists once the real root is mounted and systemd is running. An empty previous boot points at the first half of the chain; a full one that ends badly points at the second.

1. Firmware, and the fact that it does not know what Linux is

On a UEFI machine — which is every machine you are likely to be installing on now — the firmware does not scan disks for something bootable. It reads a list of boot entries out of its own non-volatile memory. Each entry names a disk, a partition, and a path to a file. That is the whole mechanism.

Those variables are visible from a running Linux system, because the kernel exposes them as files:

efibootmgr -v

BootCurrent: 0001
Timeout: 1 seconds
BootOrder: 0001,0002,0000
Boot0000* Windows Boot Manager	HD(1,GPT,...)/File(\EFI\Microsoft\Boot\bootmgfw.efi)
Boot0001* Fedora	HD(1,GPT,...)/File(\EFI\fedora\shimx64.efi)
Boot0002* UEFI: Built-in EFI Shell

BootOrder is the list the firmware works down. BootCurrent is the one that actually ran this time. BootNext, if set, overrides the order exactly once and then clears itself — which is the safe way to test a new boot path on a machine you cannot easily get to physically.

The file it points at lives on the EFI System Partition: a FAT32 partition with a specific GPT type GUID, conventionally mounted at /boot/efi on Fedora and Debian-family systems, or at /efi where the newer systemd conventions are followed. It has been getting larger — Fedora raised its minimum to 500 MB in Fedora 39, matching what Windows uses — because kernels and initramfs images are increasingly stored there rather than on a separate /boot.

One check settles which world you are in, and it matters because almost every piece of boot advice on the internet applies to only one of them:

[ -d /sys/firmware/efi ] && echo "UEFI" || echo "BIOS/CSM"

If that directory does not exist, the machine booted in legacy BIOS mode, there are no EFI variables, efibootmgr will not work, and everything in the next section does not apply to you. Legacy BIOS is not quite dead: Intel said it would stop shipping CSM support around 2020, but on the software side Fedora’s proposal to deprecate legacy BIOS was withdrawn and never landed, RHEL 10 has not removed it, and openSUSE Leap 16 still supports it — while noting that some features, full-disk encryption bound to a TPM among them, are simply unavailable there.

2. shim, and what Secure Boot is actually checking

Notice that the Fedora entry above points at shimx64.efi, not at GRUB. That indirection exists for one reason: the firmware trusts a small set of certificates burned in by the manufacturer, and in practice that means Microsoft’s. No Linux distribution is going to get its own key into every motherboard in the world.

shim is the workaround, and it is deliberately tiny. Microsoft signs it. It carries the distribution’s own certificate inside itself, and uses that to verify whatever it loads next. So the firmware trusts Microsoft, Microsoft trusts shim, and shim trusts Fedora — a chain of three, of which only the middle link needed anyone else’s cooperation.

Two things hang off shim that are worth knowing by name. MOK, the Machine Owner Key list, lets you enrol your own certificate through MokManager — the blue screen you get on the next reboot after installing a third-party kernel module such as an out-of-tree graphics or VPN driver. SBAT is the revocation mechanism: rather than blacklisting individual binary hashes, which quickly becomes unmanageable, it records generation numbers per component so a whole class of known-vulnerable bootloaders can be refused at once.

Secure Boot has a second, quieter consequence. On EFI x86 and arm64 systems the kernel’s lockdown LSM is enabled automatically when the machine booted in Secure Boot mode. Lockdown blocks the things that would let root rewrite the running kernel — loading unsigned modules, /dev/mem, some kexec paths, certain debugging interfaces. Ubuntu has done this since 20.04, and the Red Hat family does it too. It is a common cause of “this worked on my other machine”: the module is fine, the kernel simply will not load it.

# the whole picture in one command
bootctl status

# and the kernel's own view of lockdown
cat /sys/kernel/security/lockdown
none [integrity] confidentiality

The certificates at the root of all this expire in 2026. Microsoft’s 2011 UEFI CA and KEK reached the end of their lives in June 2026, replaced by a 2023 generation. This is the biggest boot-path story of the year and it is easy to miss, because nothing breaks on the day.

What actually happens is slower and worse. Machines whose firmware certificate stores are never updated keep booting exactly as they do today — but they stop being able to accept new signed boot components, because shim updates issued from late 2026 onward are signed with the 2023 CA. The failure mode is a security update you cannot install, on a machine that appears to be working perfectly.

The distribution answer is firmware updates delivered through fwupd, which on Ubuntu is rolling the 2023 CA out from version 2.0.0 onward. If you administer machines with Secure Boot enabled, fwupdmgr get-updates is worth a place in your patching routine this year in a way it was not previously.

3. The bootloader, and the config file that does nothing

Despite a decade of predictions, GRUB is still the default essentially everywhere: Fedora, RHEL 10, Debian 13, Ubuntu 26.04, openSUSE Leap 16. Arch ships no bootloader at all — its base package deliberately contains neither a bootloader nor an initramfs generator, which is why an Arch install involves choosing both. Among distributions you are likely to meet, Pop!_OS is the notable one that defaults to systemd-boot instead, managed by its own kernelstub tool.

Fedora’s position is worth recording because it was decided recently and went the other way from what many expected. In July 2026 FESCo accepted a grub2-light change for Fedora 45 — a stripped-down, UEFI-only GRUB that boots Unified Kernel Images — in preference to adopting systemd-boot, on the argument that GRUB has had far more scrutiny and fuzzing. It is additive rather than a replacement, and the debate was not unanimous, but the direction is clear: GRUB is not going anywhere in the Red Hat family.

What the bootloader has to produce is three things: a kernel, an initramfs, and a command line. Where it gets them from is the part that has quietly changed.

On Fedora and RHEL, editing /etc/default/grub does nothing. If you take one practical thing from this section, take that one — because the failure is silent. You follow instructions that were correct for years, get no error, reboot, and the setting has not taken effect.

Since Fedora 30, those distributions use the Boot Loader Specification. Each kernel gets a small file in /boot/loader/entries/ holding its own title, kernel path, initrd path and command line, and GRUB reads them through its blscfg module. grub2-mkconfig no longer generates the menu from /etc/default/grub; the BLS files are the source of truth.

The correct way to change a kernel parameter there is grubby, which edits those files directly:

grubby --update-kernel=ALL --args="transparent_hugepage=never"
grubby --update-kernel=ALL --remove-args="quiet rhgb"
grubby --info=ALL | head

# if you must edit /etc/default/grub, push it into the BLS snippets:
grub2-mkconfig -o /boot/grub2/grub.cfg --update-bls-cmdline

One related trap on the same systems: /boot/efi/EFI/fedora/grub.cfg is a stub that forwards to /boot/grub2/grub.cfg. Editing the one on the ESP, which is the one most guides tell you to write to, achieves nothing either.

BLS is no longer a systemd-specific idea, incidentally — it is a UAPI Group specification, which is why Fedora can use it under GRUB while systemd-boot uses it natively. It defines two kinds of entry: Type #1, the .conf text files described above, and Type #2, a Unified Kernel Image dropped into /EFI/Linux/ with no configuration file at all.

4. Unified Kernel Images, and why they are appearing everywhere

A UKI is the kernel, the initramfs, the command line, the microcode and a copy of os-release packed into a single UEFI executable, with a small stub program (systemd-stub) at the front that knows how to take itself apart and start the kernel inside. Each piece is a named PE section: .linux, .initrd, .cmdline, .osrel, .uname.

The reason this matters is not tidiness. It is that a UKI can be signed as one object. In the traditional arrangement Secure Boot verifies the bootloader, and the bootloader verifies the kernel, but the initramfs and the kernel command line are unsigned — and both are excellent places to subvert a machine. Fold them into the signed binary and that gap closes.

That in turn is what makes TPM-bound disk encryption practical. Because the whole boot chain is measured and the expected measurements can be calculated in advance, a disk can be unlocked automatically by the TPM only when the machine booted exactly the software it is supposed to — no passphrase, but also no unlocking if someone swapped the kernel. ukify builds these (added in systemd 253) and systemd-measure generates the signed policy that makes an update survive without re-enrolling anything.

# what is actually in a UKI
objdump -h /boot/efi/EFI/Linux/*.efi | grep -E '\.(linux|initrd|cmdline|osrel|uname)'

# what the running system booted from
bootctl list

Where this stands in practice, as of late 2026: nobody boots UKIs by default across the board, but they are no longer exotic. Fedora’s support is opt-in and can boot a UKI directly from shim with no bootloader in between at all. Ubuntu 26.04 uses a UKI in one specific and increasingly common case — its TPM-backed full-disk encryption install path, which was experimental from 23.10 and reached general availability in 26.04. If you enable that option, your kernel and initramfs live on the ESP inside a single signed file, and a lot of traditional advice about regenerating initramfs images stops applying cleanly.

5. The kernel, and the filesystem that arrives in a cpio archive

The kernel decompresses itself, sets up memory management, brings up the other CPUs, and initialises the drivers built into it. Then it faces the problem that defines the next two stages: it has no driver for your root filesystem. Not because it cannot, but because a distribution kernel that contained every storage, RAID, encryption and filesystem driver built in would be enormous, so almost all of them are modules — and the modules live on the disk that cannot yet be read.

The initramfs breaks the circle. It is a gzipped cpio archive, loaded into memory by the bootloader, which the kernel unpacks into a small filesystem in RAM. If that filesystem contains a file called /init, the kernel executes it as PID 1 and userspace has begun.

The terminology is a genuine muddle, and worth getting straight once. An initrd, historically, was a filesystem image presented as a block device; you mounted it, and control eventually returned to the kernel. An initramfs is an archive unpacked into a RAM filesystem, and its /init never returns — it is the first process, and it is responsible for everything that follows. Every mainstream distribution has used initramfs for many years. The word “initrd” survives only as the filename, in /boot/initrd.img-* and in the bootloader directive that loads it. Linux 7.0 finally removed the last of the old linuxrc-based initrd code.

# look inside the one you are running with
lsinitrd /boot/initramfs-$(uname -r).img | head -40      # dracut
lsinitramfs /boot/initrd.img-$(uname -r) | head -40      # initramfs-tools

# and the completely generic way, which always works
mkdir /tmp/ir && cd /tmp/ir
zstd -dc /boot/initramfs-$(uname -r).img | cpio -idmv 2>/dev/null

6. Early userspace, and the great dracut migration

What runs inside the initramfs has one job: assemble whatever is needed to reach the real root, mount it, and get out of the way. On a laptop that might be loading an NVMe driver and unlocking a LUKS volume. On a server it might be assembling a RAID array, activating LVM, or bringing up a network interface to reach an iSCSI target.

Which tool built it has become a live question again, after years of stability. There are three generators in common use, and one of them just moved:

GeneratorUsed byRebuild with
dracutFedora, RHEL, openSUSE, and now Ubuntudracut -f, or dracut --regenerate-all -f
initramfs-toolsDebian; Ubuntu until recently, still installableupdate-initramfs -u -k all
mkinitcpioArchmkinitcpio -P

Ubuntu made dracut the default on Desktop in 25.10 and across the board in 26.04 LTS, replacing initramfs-tools. That is the largest change to early userspace on a Debian-family system in a very long time, and it lands in a long-term-support release, so it will be the assumption for years. Debian itself is still on initramfs-tools; a wishlist bug proposing the same switch was filed in 2025, and I have not been able to confirm any decision on it, so do not assume the two behave alike any more.

The practical consequence for anyone with notes: the command you type to rebuild an initramfs on Ubuntu changed, and so did the way you break into one to debug it. rd.break is dracut-specific — it was never an initramfs-tools option, and now it is the one that works on Ubuntu.

# dracut: stop at a named point and give you a shell
rd.break=pre-mount        # also: cmdline, pre-udev, pre-trigger, initqueue, mount, pre-pivot, cleanup
rd.debug rd.shell         # trace everything, and drop to a shell if root will not mount

# initramfs-tools
break=premount            # also: top, modules, mount, bottom, init
debug=vc                  # log to the console rather than a file you cannot read

# mkinitcpio
break=premount            # or break=postmount
rd.debug

Add one of those to the kernel line by pressing e at the GRUB menu. The shell you land in is inside the initramfs, before the real root exists — so the tools available are only the ones baked into the image, and anything you were hoping to read off the disk is not there yet. That constraint is the entire point: it is where you find out why the disk is not there yet.

7. The switch to the real root, which is stranger than it sounds

Once the real root is mounted — at /sysroot, by convention — the initramfs has to hand over. It cannot simply chroot, because it is already PID 1 and the root filesystem it is standing on is the one that needs to disappear.

The traditional mechanism is switch_root, and its man page contains a sentence that surprises people the first time they read it: it recursively deletes every file on the current root filesystem. That is not vandalism. The initramfs lives in RAM, and deleting its contents is precisely how that RAM is handed back. The old root is not unmounted and set aside; it is destroyed.

Where systemd is in the initramfs — which is now the common case — the sequence has names you will see in logs, and they are worth recognising because a hang here produces messages that look like nothing else: initrd.target is the default target inside the initramfs, sysroot.mount mounts the real root, initrd-parse-etc.service reads /sysroot/etc/fstab looking for a separate /usr, and finally initrd-switch-root.target does the handover. The same PID 1 then re-executes itself from the real root’s systemd.

This stage changed in Linux 7.0, in a way that has not yet reached distributions but will. The kernel now creates a minimal nullfs as the true root with the initramfs layered above it, which means pivot_root() — the clean, ordinary mechanism, previously unusable from an initramfs — simply works:

chdir(new_root);
pivot_root(".", ".");
umount2(".", MNT_DETACH);

The old dance required quiescing the system and re-executing PID 1; one report measured around two seconds of boot time saved by dropping it. As of now systemd still ships initrd-switch-root.target and no distribution has switched over, so this is something to expect rather than something to look for — but if you are reading kernel documentation that no longer matches your system’s behaviour, this is why.

A close relative worth knowing about, since it uses the same machinery deliberately: systemctl soft-reboot, added in systemd 254. It shuts down all of userspace and switches into a new root filesystem at /run/nextroot/, without rebooting the kernel, the firmware or the bootloader. On a machine where POST takes forty seconds, applying a userspace update this way is the difference between a maintenance window and a hiccup.

8. systemd, and the difference between needing and waiting

PID 1 on the real root reads default.target — normally an alias for graphical.target or multi-user.target — and activates everything it depends on. The chain underneath is graphical.target pulling in multi-user.target, which pulls in basic.target, which pulls in sysinit.target, where the low-level work happens. systemd Beyond Services covers units in general; the part that matters for booting is one distinction.

Wants= and Requires= say what must be started. Before= and After= say in what order. They are entirely independent, and the man page says so explicitly. A unit can require another and still race it. This single misunderstanding produces most “works when I start it by hand, fails at boot” reports: the dependency is declared, so the other thing does start, but nothing said to wait for it.

# what took the time
systemd-analyze time
systemd-analyze blame | head -20

# what was actually on the critical path, which is the more useful question
systemd-analyze critical-chain
systemd-analyze critical-chain nginx.service

# a picture, if you are presenting the problem to someone else
systemd-analyze plot > boot.svg

blame is the one everybody reaches for and the one most likely to mislead: it cannot report meaningfully on Type=simple services, because systemd considers those started the instant they are forked. A service that takes ninety seconds to become useful will show up as taking a millisecond. critical-chain is the honest answer to “why is this boot slow”, because it shows only what actually delayed the next thing.

For a boot that is not slow but stuck, a different command: systemctl list-jobs shows what is still waiting, and the entry marked running at the bottom of a long list of waiting entries is the thing everything else is blocked on.

Two recent changes are worth noting if you maintain older systems. systemd 260, in March 2026, removed SysV init script compatibility entirely — an /etc/init.d script that has been quietly working for fifteen years will simply not run. Version 255 finished the /usr merge and dropped support for unmerged systems. The current release as of mid-2026 is 262.

9. getty, and the prompt itself

The last handoff is the smallest. getty@tty1.service — or a display manager on a desktop — opens a terminal device, prints the contents of /etc/issue, and waits. When you type a username it executes login, which authenticates you through PAM, sets up your session, and finally execs your shell.

The reason to know this is that a machine which reaches a login prompt and refuses your correct password has failed somewhere entirely different from one that never reaches the prompt. The first is a PAM, home-directory or account problem. The second is a boot problem. They share no diagnosis.

A change coming in Fedora 45 will make early boot look different without changing what it does: the kernel console fbcon is being replaced by the userspace kmscon. If you have learned to read a machine’s health from the exact appearance of its early text output, that appearance is about to change.

10. A worked diagnosis

A server is rebooted for a kernel update. It does not come back. Twenty minutes later someone opens the remote console and finds it sitting at an emergency shell, asking for the root password.

The instinct is to blame the kernel update, and the instinct is nearly always wrong. Start by asking how far it got.

How far did it get? Log in at the emergency shell and look at the boot that failed:

journalctl -b -1 --no-pager | tail -50
journalctl -b -1 -p err

There is a previous boot, and it is full of ordinary kernel messages. That single fact eliminates stages one through six: the firmware found the entry, shim passed it on, GRUB loaded the new kernel, the kernel unpacked the initramfs, early userspace found the root filesystem, and the switch happened — because none of the journal would exist otherwise. The new kernel is fine. Whatever went wrong is in systemd’s territory.

What is systemd unhappy about?

systemctl --failed
systemctl list-jobs

  UNIT                    LOAD   ACTIVE SUB    DESCRIPTION
 srv-backups.mount       loaded failed failed /srv/backups
 local-fs.target         loaded failed failed Local File Systems

A mount unit failed, which failed local-fs.target, which is a dependency of basic.target, which everything else waits behind. systemd waited its default ninety seconds for the device, gave up, and dropped to emergency.target — which, unlike rescue.target, deliberately pulls in nothing at all, because it cannot assume any filesystem is trustworthy. That is why the machine looks so much more broken than it is.

Which device, and where did it go?

systemctl status srv-backups.mount
grep backups /etc/fstab
blkid | grep -i backup
lsblk -o NAME,SIZE,FSTYPE,UUID,MOUNTPOINT

The UUID in fstab does not appear in blkid output. The volume is present and healthy — but somebody rebuilt that array three weeks ago, which gave the new filesystem a new UUID, and nobody rebooted in between. The change that broke this boot happened three weeks before the boot that revealed it. That gap is the reason “what did you just change?” is such an unreliable question about boot failures: fstab, initramfs contents and bootloader configuration are only read at boot, so a mistake in any of them lies dormant until the next reboot, which may be months away.

The immediate fix is the current UUID in fstab. The durable fix is nofail on every mount that is not required for the system to function:

UUID=...  /srv/backups  ext4  defaults,nofail,x-systemd.device-timeout=10s  0 2

# and always, before you walk away from an fstab edit:
systemctl daemon-reload
mount -a

With nofail, a missing backup volume becomes a warning in the journal instead of a server that will not boot. It is one word, and it is the difference between an inconvenience and a callout.

Notice what the method bought. Had journalctl -b -1 been empty, the entire second half of the chain would have been eliminated instead, and the next questions would have been efibootmgr -v and bootctl status from a live image — does the firmware entry still exist, does the file it points at still exist, is the ESP even mounted. One question, asked first, cuts the search space in half.

11. Symptom, stage, command

What you seeStage and likely causeWhat to run
“No bootable device” from the firmware1 — the boot entry or the ESP is goneefibootmgr -v from a live image; check the ESP is present
Boots to the firmware setup screen every time1 — BootOrder lost or the entry is lastefibootmgr -o 0001,0000
Worked, then stopped after a firmware update1 — firmware update cleared the EFI variablesRe-run the distro’s bootloader install; efibootmgr -c
“Verification failed” or “Access denied” before the menu2 — Secure Boot rejected the next stagebootctl status; check for a pending MOK enrolment
A third-party module will not load, no error in the module2 — kernel lockdown under Secure Bootcat /sys/kernel/security/lockdown
A signed update refuses to install on an older machine2 — the 2011 Microsoft CA has expiredfwupdmgr get-updates; update firmware certificates
A kernel parameter has no effect on Fedora or RHEL3 — /etc/default/grub is not the source of truthgrubby --info=ALL; set it with grubby --args=
The GRUB menu lists a kernel that no longer exists3 — a stale BLS entryls /boot/loader/entries/; remove the orphan
Menu appears, then a black screen and nothing3 or 4 — kernel loaded, no console outputRemove quiet and rhgb/splash from the kernel line
Kernel panic: “unable to mount root fs”5 — the initramfs lacks the driver for the root deviceBoot the old kernel; lsinitrd the new image; rebuild it
Dropped to a dracut:/# prompt5 — the root device never appearedlsblk, cat /proc/cmdline, dmesg | tail in that shell
Hangs after “Reached target Initrd Root Device”5 — waiting on a device, LUKS or RAID that never came upReboot with rd.break=pre-mount rd.debug
Root filesystem is mounted read-only at boot5 or 6 — fsck failed, or the switch never completed cleanlyjournalctl -b -1 -p err; fsck from a live image
Boot stalls for exactly ninety seconds, then continues7 — a unit hit the default timeoutsystemd-analyze blame; add nofail if it is a mount
Emergency shell asking for the root password7 — local-fs.target failed, usually an fstab entrysystemctl --failed; journalctl -b -1 -p err
Boot hangs with no message at all7 — one job blocking everything behind itsystemctl list-jobs — find the one marked running
A service works by hand and fails at boot7 — Wants= without After=systemctl show unit -p After -p Wants
An /etc/init.d script stopped running after an upgrade7 — systemd 260 removed SysV compatibilityWrite a unit file; systemctl status will show it is unknown
Boot is slow and blame shows nothing large7 — Type=simple services are not measured usefullysystemd-analyze critical-chain
Login prompt appears but the password is refused8 — not a boot problem at all: PAM, account or homejournalctl -u systemd-logind; passwd -S user
Machine boots but the previous boot left no journal— the journal is volatilemkdir -p /var/log/journal and systemd-journald will persist it

That last row is worth acting on before you need it. If journalctl -b -1 says there are no previous boots, the journal is being kept in RAM only, and every failed boot is erasing its own evidence. One directory fixes it permanently, and it is the single best preparation for the next problem on this list.

12. The shape of the whole thing

The chain is a series of narrowing contexts. The firmware knows only about partitions and files. shim knows only about signatures. The bootloader knows about kernels but not about your system. Early userspace knows how to find one filesystem and nothing else. Only once systemd starts on the real root does anything have a complete picture — and by then every stage that could have failed has already deleted itself.

Which is why the diagnostic discipline is the same every time, and why it starts with a question rather than a command: how far did it get? A journal from the failed boot means the first six stages worked and you are looking at units. No journal at all means the opposite, and you want a live image, efibootmgr and bootctl. Everything else on this page is detail hanging off that one split.

The other thing worth carrying away is the delay. Bootloader configuration, fstab and initramfs contents are read once, at boot, and at no other time. A mistake in any of them is invisible until the next reboot — which on a server may be a quarter of a year later, long after anyone remembers making it. Rebooting a machine deliberately, while you are sitting in front of it and remember what you changed, is not caution. It is the only way to find out whether the change worked.

Next, if this was useful: Permissions and Privilege, Properly does the same thing for the six layers between a process and a file, and The Life of a Packet for everything between an application and the wire. For the encryption side of stage five, disk encryption on a Linux laptop covers LUKS and what it does and does not protect.