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 debian
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.
Now systemd-boot is installed, but it does not know about the installed kernel images in the system.
The following script updates 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.
You will have to change the content of the root_disk variable to use the UUID of the root disk. You can use the blkid command to find the correct id.
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. Find yours with `sudo blkid`
root_disk="UUID=xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
# 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
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.