Using systemd-boot on Debian Bullseye
This post describes how to replace Grub with the systemd-boot UEFI boot manager. The setup is very similar to the method described in the article Replace GRUB2 on Ubuntu 10.04 with a few simplifications (no secure boot) and with a slightly improved kernel installer hook script.
Start by creating a boot config file /boot/efi/loader/loader.conf with the following content.
default systemd
timeout 1
editor 1
Create the directory that will hold the configuration files for the installed kernel images
mkdir -p /boot/efi/loader/entries
and install the boot loader with the bootctl command
bootctl install --path=/boot/efi
You can then check if the installation was successful with the efibootmgr. This command is also useful to change the boot order and to delete old boot entries. The systemd-boot entry should show up as "Linux Boot Manager".
Now systemd-boot is installed, but it does not know about the installed kernel images in the system.
Use the kernel-install command to add and remove the kernel to and from the systemd-boot directory structure. Simply create a /etc/kernel/postinst.d/zz-update-systemd-boot file with the following content:
#!/bin/sh
set -e
/usr/bin/kernel-install add "$1" "$2"
exit 0
Create a /etc/kernel/postrm.d/zz-update-systemd-boot file with the following content:
#!/bin/sh
set -e
/usr/bin/kernel-install remove "$1"
exit 0
Create the /boot/efi/<machine-id> directory if it doesn't exist (find the machine-id from the file /etc/machine-id) and run kernel-install manually to force the initial install. That command will look like kernel-install add `uname -r` /boot/vmlinuz-`uname -r`.
Now you should see all installed kernel images in the systemd-boot menu.
It is not necessary to remove grub, as long as it appears after systemd-boot in the output of efibootmgr. This also provides a useful fallback in case systemd-boot is having a problem.
Legacy install script
If the kernel-install command is not available, use the following script to update the systemd-boot configuration with the installed kernel images. Save the script to a system location, like /usr/local/bin/update-systemd-boot.sh and call it every time a new kernel is installed or removed from the system.
Also check the kernel parameters in the flags variable if you need special arguments passed to the kernel.
#!/bin/sh
#
# This is a simple kernel hook to populate the systemd-boot entries
# whenever kernel images are added or removed.
#
# The disk containing the root partition; also see `sudo blkid`
root_disk="UUID=$(findmnt / -o UUID -n)"
# The linux kernel arguments
flags="ro quiet"
prog_name=`basename $0`
boot_dir="/boot"
sd_boot_dir="loader/entries"
sd_boot_path="$boot_dir/efi/$sd_boot_dir"
title=
prefix=
force=0
verbose=0
usage() {
echo "$prog_name [OPTIONS]"
}
help() {
usage
echo
echo "OPTIONS"
echo " -h show this help page"
echo " -f force overwrite"
echo " -v make verbose"
}
while getopts hfv opt; do
case "$opt" in
h) help
exit 0;;
f) force=1;;
v) verbose=1;;
\?) # unknown flag
usage >&2
exit 1;;
esac
done
shift `expr $OPTIND - 1`
if [ $# -ne 0 ]; then
echo >&2 "No extra argument allowed"
exit 1
fi
sd_boot_entry() {
cat <<-EOF
title $title
version $version
linux /$sd_boot_dir/vmlinuz-$version
initrd /$sd_boot_dir/initrd.img-$version
options root=$root_disk $flags
EOF
}
if [ ! -h "/dev/disk/by-uuid/${root_disk#UUID=}" ]; then
echo >&2 "error: root disk '$root_disk' not found"
exit 1
fi
if [ ! -d "$sd_boot_path" ]; then
echo >&2 "error: directory '$sd_boot_path' not found"
exit 1
fi
if [ ! -f /etc/machine-id ]; then
echo >&2 "error: machine id file '/etc/machine-id' not found"
exit 1
fi
prefix="`cat /etc/machine-id`-v"
if [ -f /etc/os-release ]; then
title=`sed -ne '/^PRETTY_NAME=/s/.*"\(.*\)".*/\1/p' /etc/os-release`
else
title=Debian
fi
echo "Updating Systemd boot entries"
# Copy images from the Debian install directory to the EFI partition
find "$boot_dir" -maxdepth 1 -type f -name '*.dpkg-tmp' -prune -o -name 'vmlinuz-*' -exec cp -u {} "$sd_boot_path" \;
find "$boot_dir" -maxdepth 1 -type f -name '*.dpkg-tmp' -prune -o -name 'initrd.img-*' -exec cp -u {} "$sd_boot_path" \;
# Remove files from the EFI if they are missing from the Debian install directory
find "$sd_boot_path" -maxdepth 1 -type f -name 'vmlinuz-*' | while read i; do
kernel="$boot_dir/`basename $i`"
if [ ! -f "$kernel" ]; then
rm -f "$i"
fi
done
find "$sd_boot_path" -maxdepth 1 -type f -name 'initrd.img-*' | while read i; do
initrd="$boot_dir/`basename $i`"
if [ ! -f "$initrd" ]; then
rm -f "$i"
fi
done
# Remove the conf file
find "$sd_boot_path" -maxdepth 1 -type f -name "$prefix*.conf" | sort -Vr | while read i; do
version=`basename $i | sed -e "s/$prefix//; s/.conf$//"`
kernel="$sd_boot_path/vmlinuz-$version"
initrd="$sd_boot_path/initrd.img-$version"
if [ ! -f "$kernel" -o ! -f "$initrd" ]; then
echo "Removing kernel v$version"
rm -f "$i" "$kernel" "$initrd"
fi
done
# Add new kernel entries
find "$boot_dir" -maxdepth 1 -type f -name '*.dpkg-tmp' -prune -o -name 'vmlinuz-*' -print | sort -Vr | while read i; do
version=`basename $i | sed -e 's/vmlinuz-//'`
initrd="$boot_dir/initrd.img-$version"
file="$sd_boot_path/$prefix$version.conf"
echo "Found kernel `basename $i`"
if [ ! -f "$initrd" ]; then
echo "Ignoring $i"
elif [ $force -eq 1 -o ! -f "$file" ]; then
echo "Adding $file"
sd_boot_entry "$version" > "$file"
cp -v $i "$initrd" "$sd_boot_path"
fi
done
Calling the update script every time a kernel is installed or removed can be tedious, and luckily this can be automated. Simply create the following two scripts /etc/kernel/postinst.d/zz-update-systemd-boot and /etc/kernel/postrm.d/zz-update-systemd-boot with this content:
#! /bin/sh
set -e
sh /usr/local/bin/update-systemd-boot.sh
exit 0
Update 25 February 2021
Find the disk UUID using the findmnt command. Thanks to Björn Beckendorf for this tip.
Update 29 August 2021
Use the kernel-install command to add and remove the kernel image. Thanks to Alan MacLeod and Andrea Pappacoda for this tip.