[How To] Convert to systemd-boot

Convert GRUB to systemd-boot

This tutorial has originally been written by @dalto and published at EndeavourOS forum (see link at the bottom).

As the general principle applies to Manjaro as well, I have - with proper permission - reproduced the guide to fit into the Manjaro knowledge base. My rewrite is kept as close to the original as possible only replacing the original authors self reference with more generic phrases.

Should you apply this to your system? That is a question I cannot answer - only you can.

A couple of reasons to install systemd-boot

  • systemd-boot is less complicated than grub. It uses simple text based config files which only contain a few lines
  • It is less prone to breakage, easy to troubleshoot and doesn’t require any process to rebuild config files

BUT - simplicity comes with limitations

  • It only supports UEFI
  • It doesn’t have all the advanced features of grub (e.g. btrfs snapshot boot)
  • Your kernels must be kept inside your $esp (EFI System Partition) so it needs to be large enough to hold them all

To be clear, systemd-boot won’t add any new functionality to your system. It is really for those who prefer simplicity or just like trying new things.

systemd-boot is quite simple to install. However, as the guide will be presenting two options for managing it, reading and understanding the guide takes a bit longer than the actual process itself.

It generally should only take 5-10 minutes to convert your setup.

IMPORTANT NOTES:

  • It is of critical importance that you not stop halfway or reboot during the process. Once you start converting your system you need to finish it. If you don’t, your system won’t boot.
  • If you get an error, do not ignore it and keep going. Stop and fix the error or ask for help.
  • It is fundamental to ensure your system is up-to-date and have been restarted before you begin your endeavour.
  • Whenever you mess with your bootloader, it is a good idea to have a bootable ISO ready in the event something goes wrong.
  • If you use LUKS on any partitions please see the section at the bottom on special handling for LUKS

Install systemd-boot

The first thing is to ensure your $esp partition is large enough. Run the command du -sh /boot and compare that to the size of your EFI partition. Your EFI partition needs to be larger than the output of the command. If it is, you can continue. Before going any further you need to enlarge the partition.

IMPORTANT Enlarging may not be an option due to lack of adjacent space. How you solve this cannot be predicted - but resizing the root partition almost always result in UUID change and possibly an unbootable system. The best method is to remove unused kernels before continuing.

Next we need to remove grub:

sudo pacman -Rc grub

The steps required are

  • create a new folder to mount the $esp
  • mount $esp on new location (unmount old location)
  • move the existing kernels and images from /boot into /efi

The partition can be mounted at /boot or /efi. This is a folder clash so this guide will be using /efi as the new mountpoint.

First create the new efi folder

sudo mkdir /efi

Create a variable (efidevice) for the terminal session holding the identity of your $esp partition

efidevice=$(findmnt /boot/efi -no SOURCE)

Unmount $esp from the current mountpoint

sudo umount /boot/efi

Mount the $esp on the new location

sudo mount ${efidevice} /efi

To make the mount change permanent, edit /etc/fstab and change where it reads /boot/efi to
/efi

Next we can install systemd-boot

 $ sudo bootctl install
Created "/efi/EFI/systemd".
Created "/efi/loader".
Created "/efi/loader/entries".
Created "/efi/EFI/Linux".
Copied "/usr/lib/systemd/boot/efi/systemd-bootx64.efi" to "/efi/EFI/systemd/systemd-bootx64.efi".
Copied "/usr/lib/systemd/boot/efi/systemd-bootx64.efi" to "/efi/EFI/BOOT/BOOTX64.EFI".
Random seed file /efi/loader/random-seed successfully written (32 bytes).
Created EFI boot entry "Linux Boot Manager".

Edit the file /efi/loader/loader.conf and uncomment the “timeout” line like so. Save the file

 $ cat /efi/loader/loader.conf 
timeout 3
#console-mode keep

Technically speaking, you have now successfully installed systemd-boot.

Of course, since no boot entries or other config has been added, if you reboot now you will be sitting at a screen where the only option is to enter your firmware interface :scream: and you don’t wanna :cry:

In systemd-boot, the boot menu options are called loader entries. An entry needs to be created for each kernel/initramfs combo. There are two ways to do this. There is the legacy way where we manually create kernel entries or we can used the more modern approach of using systemd-boot’s built-in kernel-install command. Here are the pros/cons of each approach.

Manual

  • Requires less changes to the system, file locations for kernels and initrds will be at the locations you are accustomed to
  • You will need to manually create entry files for each kernel you install
  • If you multi-boot, kernel collision can occur unless you work around this manually

kernel-install

  • A more modern approach
  • Completely automates the process of entry creation and removal
  • Safe for multi-booting since it moves your kernels and images to a installation specific subdirectory
  • It may take some adapting to the fact that /boot will now be empty

Once you choose which option you prefer, you can follow the instructions below.

ANOTHER IMPORTANT NOTE: Don’t try to follow both sets of instructions. You must choose either legacy/manual or kernel-install

Manual

Move your kernels and images to /efi and use a symbolic link to connect your efi partition to /boot. Alternatively, you could just mount your efi partition on /boot instead of /efi. If you do that, you will need to alter the below instructions a bit.

sudo mv /boot/*.img /boot/vmlinuz* /efi/.
sudo rm -r /boot # cleanup /boot
sudo ln -s /efi /boot

Next - either create a hook to automatically keep systemd-boot up-to-date following the instructions here OR install the AUR package systemd-boot-pacman-hook which does the exact same thing. For this example, we will use the AUR package:

pamac build systemd-boot-pacman-hook

Now you must create an entry conf file for each kernel as described here. If you want fallback entries you must add these as well. Don’t forget to add initrd lines for your microcode. If you want to know what you current kernel options are you can see them with cat /proc/cmdline. The initrds get their own line and the rest goes into the “option” line of the loader entry.

As an example, here are what the entries look like on the original authors test VM:
/efi/loader/entries/eos-linux.conf

title   EndeavourOS 
linux   /vmlinuz-linux 
initrd  /initramfs-linux.img 
options root=UUID=5aa3e4e1-7e37-4bd6-95ce-7324a040785b rw loglevel=3 nowatchdog 

/efi/loader/entries/eos-linux-fallback.conf

title   EndeavourOS (fallback)
linux   /vmlinuz-linux 
initrd  /initramfs-linux-fallback.img 
options root=UUID=5aa3e4e1-7e37-4bd6-95ce-7324a040785b rw loglevel=3 nowatchdog 

That is it, now you can reboot. If you install/remove any kernels, you need to add/remove the loader entries for them. As an alternative, you can look into automating this using systemd-boot-manager. The package is authored by @dalto and available in Manjaro repo. However, further automaton is possible using the kernel-install method instead.

kernel-install

The hard work of getting kernel-install fully functional on Arch based systems has been done by @dalto (the original author of this topic). A package which handles all the automation is here and can be installed onto Manjaro using the a custom package kernel-install-mkinitcpio.

The first thing to do is to build and install the custom package

pamac build kernel-install-mkinitcpio

Next thing is to ready your system for kernel-install and run kernel-install on all your installed kernels.

For that purpose run the script below as a script and run it or run the commands one at a time. If you run it manually, the mkdir and kernel-install will need to be run as with sudo. Otherwise, just run the whole script with sudo.

#!/usr/bin/env bash

# Find the configured esp
esp=$(bootctl -p)

# Prepare the efi partition for kernel-install
machineid=$(cat /etc/machine-id)
if [[ ${machineid} ]]; then
    mkdir ${esp}/${machineid}
else
    echo "Failed to get the machine ID"
fi

# Run kernel install for all the installed kernels
while read -r kernel; do
    kernelversion=$(basename "${kernel%/vmlinuz}")
    echo "Installing kernel ${kernelversion}"
    kernel-install add ${kernelversion} ${kernel}
done < <(find /usr/lib/modules -maxdepth 2 -type f -name vmlinuz)

Lastly, we can cleanup /boot

sudo rm -r /boot/efi /boot/grub /boot/initramfs* /boot/vmlinuz*

Now you can reboot into your new system. As you install/remove kernels, the whole process should be automated.

Special handling for LUKS

By default there is a keyfile in the initramfs. With systemd-boot, the initramfs is now in the ESP. This means that your keyfile is now in an unencrypted location. That is bad.

The good news is that you shouldn’t need a keyfile in your initramfs with systemd-boot so we can simply remove it. Here is how:

Carefully follow the following steps:

  • Edit /etc/mkinitcpio.conf and remove /crypto_keyfile.bin from the files section.
  • Reinstall your currently installed kernels with pacman.
  • Reboot and make sure everything is still working. You should get asked for your password.

If you no longer need the keyfile at all, you can remove it completely. Be aware, if you have more than one luks partition, removing this keyfile may cause you to be asked for your password more than once.

  • Use cryptsetup to delete the key from the luks partition
sudo cryptsetup luksRemoveKey /dev/sdxy /crypto_keyfile.bin 

Replace /dev/sdxy with the partition you have LUKS installed on.

  • Delete the keyfile from the disk - sudo rm /crypto_keyfile.bin

Keep in mind that whenever you mess around with your LUKS partition you run the risk of locking yourself out so it is wise to have a backup.

How to modify kernel options

But how can you modify kernel options as you would in grub by editing GRUB_CMDLINE_LINUX and then running grub-mkconfig.

In systemd-boot, it is actually quite simple. You edit the appropriate entry file which can be found on your EFI partition in the loader/entries directory. Each entry is a boot option on the menu and each has a line called options. Simply edit that line to suit your liking. If you are using the manual method described above, there is nothing else to do.

It is slightly more complicated if you are using the kernel-install method. The reason is that the kernel-install is generating the entries for you. So you can edit them, and that will work but the next time you install/update a kernel they will be reset. Although it isn’t fully needed to understand the details of how that works, it is worth explaining for those interested. If you just want instructions, then skip the next part.

:see_no_evil:

How kernel-install determines what kernel options to set

Generally speaking, kernel-install takes the running kernel options from /proc/cmdline unless you have specific settings in /etc/kernel/cmdline. The latter will always take precedence. So that means, by default, if you make a change to the running options or change the entry file and reboot, those changes will automatically get picked up.

This sounds great and it works great too. Well…it works great until you need to rescue your system in a chroot. Now the running kernel has the completely wrong options. If you understand(and remember) all this then when you are in the chroot you can simply copy the options line from one of your entries into /etc/kernel/cmdline and all will be good. However, if you don’t know, don’t remember or don’t have access to the existing entries, your entries will get populated with bad kernel options and your rescue attempts will have become more complicated.

The automation around kernel-install is intended to be as simple and automated as possible. The intention is also the end-user do not need to understand how it works to be able to use it. So what the hooks/scripts do is whenever a kernel is installed/updated, if /etc/kernel/cmdline doesn’t exist then the running kernel options are saved into it. That means that if you are ever in chroot you should have a good copy of the proper kernel options you need and everything should “just work” without you having to worry about it.

Enough about the details, how do I change the options?

There are actually a few different ways you can do this. Here are a couple of good ways. Keep in mind, you only need to use one of these methods, not both of them.

  • You can modify the entry file you are using manually and reboot. Once you have it working to your satisfaction, delete /etc/kernel/cmdline. The next time you install/update a kernel it will get created again with your new options.
  • You can modify /etc/kernel/cmdline and then either reinstall your kernel or call kernel-install directly to regenerate your entry. Reinstalling a kernel is probably easier to remember but if you would prefer to call it manually, an example of how you would do that is below.
kernel-install add 5.10.43-1-lts /usr/lib/modules/5.10.43-1-lts/vmlinuz

Credits

Thank you @dalto for the Original guide and the permission to reproduce it here in it’s entirety.

10 Likes

:no_entry_sign: This is a guide - don’t post questions here

:new: Create a new topic with your question linking to this guide and I will reply.

:ok: If I made a typo or other errors - please do reply

You reference the AUR package kernel-install-mkinitcpio is already in Manjaro’s repo with the name systemd-kernel-maintenance systemd-kernel-maintenance

(Source: FH / systemd-kernel-maintenance · GitLab)

As I recall - the packages are different

The repo one is a fork with a bit more Manjaro-specific branding if I recall correctly.

From the code, they are equal, however, a bit outdated. But there is a PR to update the package (especially the reinstall-kernels script is not yet included in the repo version.)

Let’s see if the maintainer wakes up :wink: (Edit: Haha, I just saw your name and initials :grin:)

1 Like

Can you check if this is still working? With the latest unstable mkinitcpio, the initrd is generated into the tmp directory (staging), and not copied to /boot.
This leads to an unbootable system.

Confirm, mkinitcpio 36-1 something is wrong with systemd-boot.
but it has no issue with Grub or EFISTUB.

This was just reported to me as well. It seems like there is some change in the latest mkinitcpio that is problematic. I will take a look at it tonight after work and try to get it updated in AUR at least.

Something is wrong with manjaro’s version of mkinitcpio. It works fine on arch. I’d look into manjaro.patch.

Did you install kernel-install-mkinitcpio from AUR ?

Edit://
I can reproduce the issue on Arch Linux when installing kernel-install-mkinitcpio.

@dalto
It looks like mkinitcpio version 36 breaks the backwards compatibility with kernel-install-mkinitcpio from AUR (systemd-kernel-maintenance for Manjaro)

Hm ok. But I don’t understand why I would need to use that package at all.

I think it is useful for multiboots on the same ESP partition.

I guess you need to use kernel-install to generate systemd-boot entries automatically?

In that case you could just use UKIs.

Yes, that’s what I did. However, you still need to tell it when to rebuild the image.
E.g. what do I run after install linux63-virtualbox-host-modules?

It boots but I’m not sure how shaky or stable this process is.

You don’t need to run anything.

It looks to me like this is an issue with mkinitcpio. The changes to 50-mkinitcpio.install seem to be causing this.

Yeah, setting up sbupdate once for making Manjaro UKIs was what I did, and since then all images are put into Linux forder, automatically discovered by systemd-boot. Mkinitcpio builds initrds, sbupdate makes kernel images with all necessary stuff

What’s your process if you update the cmdline/kernel parameters?

https://wiki.archlinux.org/title/Unified_kernel_image

It all depends how you want to do it - is it with sbctl, kernel-install, ukify, manually, etc. You can use plain mkinitcpio - just edit .preset files.

With sbctl or mkinitcpio you edit /etc/kernel/cmdline. Last version (36) of mkinitcpio even added drop-in directory for cmdline, I think.

1 Like

If I want to add a new option to cmdline, I edit the respective line in /etc/sbupdate.conf and run sudo sbupdate which re-creates UKIs with the new cmdline. Just for fun I have a separate mkinitcpio and kernels configs that I use to generate systemd-based initrds and sbupdate conf file containing some extra entries with custom cmdlines for such kernels.