Every time a Linux machine powers on, a quiet negotiation happens before the kernel ever loads. Firmware hands control to a boot loader, the boot loader finds the kernel, passes it parameters, and steps aside. That sequence takes under a second on modern hardware, yet the tool responsible for it shapes system security, maintainability, and recovery options for the entire lifetime of the installation. For years, GRUB was the only serious answer on x86 Linux. Today, systemd-boot challenges that assumption directly, and the difference between the two is not merely cosmetic.

Understanding which boot loader belongs on a given system requires going past opinion and into the mechanics of how each one actually works.

How GRUB Arrived at Its Current Architecture

GNU GRUB, now in its second major iteration and almost universally referred to as GRUB2, was designed with a guiding principle that has defined its entire development history: run on anything, boot anything. That ambition produced a boot loader that ships its own filesystem drivers, its own scripting environment, its own module loader, and its own minimal shell. When GRUB initializes on a BIOS system, it reads the first stage from the Master Boot Record, then locates and loads its core image, which may reside in the gap between the MBR and the first partition or inside a dedicated BIOS Boot Partition on GPT-formatted disks.

On UEFI systems, the flow is different but the philosophy is the same. GRUB installs a PE32+ EFI application, typically named grubx64.efi, into the EFI System Partition. The firmware loads that binary directly. From there, GRUB reads its configuration from a generated file and presents its menu to the user.

The configuration file, grub.cfg, is the operational heart of the system. It is never meant to be edited directly. Instead, it is regenerated from two sources: the human-editable settings in /etc/default/grub and the scripts distributed across /etc/grub.d/. When a new kernel is installed, the package manager triggers a regeneration. On Debian and Ubuntu systems the regeneration command is update-grub, which is itself a thin wrapper:

sudo update-grub
# Equivalent on RHEL/Fedora/Arch systems:
sudo grub-mkconfig -o /boot/grub/grub.cfg

A typical menuentry block inside the generated grub.cfg reveals how explicit and verbose GRUB's internal language is:

menuentry 'Arch Linux' --class arch --class gnu-linux --class gnu --class os {
    load_video
    set gfxpayload=keep
    insmod gzio
    insmod part_gpt
    insmod ext2
    search --no-floppy --fs-uuid --set=root a1b2c3d4-e5f6-7890-abcd-ef1234567890
    echo 'Loading Linux kernel...'
    linux /boot/vmlinuz-linux root=UUID=a1b2c3d4-e5f6-7890-abcd-ef1234567890 rw quiet
    echo 'Loading initial ramdisk...'
    initrd /boot/initramfs-linux.img
}

Every insmod call loads a module from /boot/grub/, pulling in filesystem support, video drivers, or cryptographic routines as needed. This modularity is central to GRUB's strength. It can decrypt a LUKS-encrypted /boot partition using cryptodisk, mount Btrfs subvolumes, read from XFS, NTFS, ZFS, and a dozen other filesystems, all before the kernel has any say in the matter.

What systemd-boot Chose to Be Instead

systemd-boot, which was known as gummiboot before it was merged into the systemd project, made a different choice from the outset. Its developers decided that a boot loader running on UEFI hardware has no business reimplementing filesystem drivers, scripting engines, or graphical themes. The UEFI firmware already provides a stable API. The EFI System Partition is already required to be FAT32, a filesystem the firmware itself can read. A modern boot loader, in this philosophy, should be a thin and auditable bridge between firmware and kernel, nothing more.

The result is a binary that weighs a few hundred kilobytes and has no runtime module system. Installing it takes one command:

sudo bootctl install

That command copies systemd-bootx64.efi into the ESP, registers a UEFI boot entry for it, and sets it as the first in the boot order. The entire installation state is visible immediately:

bootctl status
# System:
#     Firmware: UEFI 2.80 (American Megatrends 5.26)
#  Secure Boot: enabled (user)
#   TPM2 Support: yes
# Boot into FW: supported

Boot entries for systemd-boot are plain text files placed in $ESP/loader/entries/, one file per boot option. A complete, working entry for an Arch Linux installation looks like this:

# /efi/loader/entries/arch-linux.conf
title   Arch Linux
linux   /vmlinuz-linux
initrd  /initramfs-linux.img
options root=UUID=a1b2c3d4-e5f6-7890-abcd-ef1234567890 rw quiet

The global loader configuration, which governs timeout and default selection, lives in $ESP/loader/loader.conf:

default arch-linux.conf
timeout 4
editor  no

The editor no line is worth pausing on. By default, systemd-boot allows users to modify kernel command-line parameters at the boot menu. Disabling the editor prevents someone with physical access from appending init=/bin/bash and gaining an unauthenticated root shell. It is a small setting with significant security implications.

The Unified Kernel Image and What It Changes

The most consequential modern development in the systemd-boot ecosystem is the Unified Kernel Image, commonly abbreviated to UKI. A UKI is a single PE32+ executable that bundles the kernel, the initramfs, the kernel command line, and a UEFI boot stub into one file with a .efi extension. That bundle can be loaded directly by the firmware, signed as a unit with a single Secure Boot signature, and placed in $ESP/EFI/Linux/ where systemd-boot discovers and boots it automatically with no entry file required.

Building a UKI on Arch Linux through mkinitcpio involves defining the output paths in the kernel preset file:

# /etc/mkinitcpio.d/linux.preset
ALL_config="/etc/mkinitcpio.conf"
ALL_kver="/boot/vmlinuz-linux"
PRESETS=('default' 'fallback')

default_uki="/efi/EFI/Linux/arch-linux.efi"
default_options="--splash /usr/share/systemd/bootctl/splash-arch.bmp"

fallback_uki="/efi/EFI/Linux/arch-linux-fallback.efi"
fallback_options="-S autodetect"

After rebuilding the preset with mkinitcpio -P, the resulting .efi files appear in the ESP and are immediately recognized by systemd-boot. Kernel command-line parameters can be embedded into the UKI itself by writing them to /etc/kernel/cmdline before generation:

echo "quiet rw bgrt_disable" | sudo tee /etc/kernel/cmdline

When Secure Boot is active and the UKI is signed, the embedded command line becomes immutable. No bootloader menu interaction can override it. That property is the foundation of a genuinely tamper-resistant boot chain.

Secure Boot and the Real Difference in Implementation

Both boot loaders can participate in UEFI Secure Boot, but the paths diverge sharply in how much effort that participation requires.

GRUB ships with a pre-signed binary distributed by most Linux vendors, one that carries a Microsoft-trusted certificate already enrolled in the firmware of commercial machines. This shim-based approach means that GRUB's Secure Boot story works by default on typical hardware without any key management on the user's part. The tradeoff is that the chain of trust involves a third party whose key is not controlled by the system owner.

systemd-boot, combined with UKIs, makes a more demanding but architecturally stronger offer. The system administrator generates their own Secure Boot keys, enrolls them into the UEFI firmware, and signs the boot loader and all UKIs with those keys. The sbctl tool manages this workflow:

# Create a new set of signing keys
sudo sbctl create-keys

# Enroll the keys into UEFI firmware, including Microsoft's third-party key for hardware compatibility
sudo sbctl enroll-keys --microsoft

# Sign the boot loader and any kernel images
sudo sbctl sign --save /efi/EFI/systemd/systemd-bootx64.efi
sudo sbctl sign --save /efi/EFI/Linux/arch-linux.efi

# Verify all registered files are properly signed
sudo sbctl verify

With this configuration, only binaries signed by keys the system owner controls will boot. The firmware rejects anything else. That is Secure Boot functioning as originally envisioned, not as a checkbox for vendor certification.

What GRUB Does That systemd-boot Simply Cannot

The philosophical gap between the two tools becomes a practical constraint in several scenarios that remain firmly in GRUB's territory.

Encrypted /boot is the most common case. When the kernel and initramfs live on a LUKS-encrypted partition, something must decrypt that partition before the kernel is accessible. GRUB can do this, because it carries the cryptodisk module and can prompt for a passphrase before the kernel loads. systemd-boot cannot. It can only read from FAT32, which means the kernel and initramfs must be on the unencrypted ESP. For full-disk encryption setups that also need an encrypted boot partition, GRUB remains the only viable choice among the two.

Legacy BIOS systems are another hard boundary. systemd-boot is a UEFI-only program. It has no MBR mode, no chainloading from partition boot sectors, and no support for systems without EFI firmware. Any machine that predates UEFI or runs in CSM compatibility mode is outside its scope entirely.

Multi-boot environments with non-Linux operating systems are considerably easier to manage under GRUB, which can chainload EFI binaries, detect installed systems through os-prober, and generate unified menus automatically. systemd-boot handles this through separate entry files, which is manageable but more manual.

Configuration Scope and Day-to-Day Management

One dimension that distinguishes these tools in daily operation is how much mental overhead each one demands when the system changes.

GRUB's configuration is deliberately opaque to direct editing. The right workflow is to modify /etc/default/grub or add files to /etc/grub.d/, then regenerate:

# After editing /etc/default/grub, for example changing GRUB_TIMEOUT or GRUB_CMDLINE_LINUX
sudo update-grub          # Debian/Ubuntu
sudo grub-mkconfig -o /boot/grub/grub.cfg   # other distributions

The generated grub.cfg is long, dense, and not intended for human reading. When something breaks, diagnosing it requires familiarity with GRUB's scripting syntax and module system.

systemd-boot's configuration is always human-readable. Adding a new boot entry means creating a new .conf file. Removing one means deleting it. Updates to the boot loader binary itself are handled through a systemd service that runs at boot:

# Manually update the boot loader binary in the ESP
sudo bootctl update

# Check the current status and list all available boot entries
bootctl list

The output of bootctl list is structured and informative, showing each entry's type, source path, and any issues detected. That transparency is, in practice, one of the reasons administrators who have migrated to systemd-boot tend not to migrate back.

Choosing Between Them Without Guesswork

The honest answer to which boot loader is better is that the question is malformed. A more useful question is which one matches the system's requirements without carrying unnecessary weight.

The practical criteria break down like this:

  • GRUB is the appropriate choice for legacy BIOS hardware, systems requiring an encrypted /boot partition, complex multi-boot setups with non-Linux operating systems, and any environment where filesystem support beyond FAT32 is needed before the kernel loads.
  • systemd-boot is the appropriate choice for UEFI-only single-OS or dual-OS setups, systems where Unified Kernel Images and custom Secure Boot keys are desirable, and administrators who want boot configuration that is transparent, auditable, and trivially maintainable.

Neither is inherently superior. GRUB's complexity is not a flaw; it is the necessary cost of its generality. systemd-boot's limitations are not bugs; they are the deliberate consequence of a design that refuses to solve problems it was not asked to solve.

What the existence of both tools reflects is something healthy about the Linux ecosystem: the recognition that a boot loader, like any other system component, should be sized to its job. A minimal, UEFI-native system gains nothing from GRUB's module loader and filesystem zoo. A server with an encrypted boot volume gains nothing from systemd-boot's flat entry files. The question was never which tool wins. It was always whether the administrator understood their system well enough to ask.