Timing boot with systemd

May 30, 2017

Being an Arch Linux user pretty much means that for better for worse, I am stuck using systemd as my init system, whether I like it or not. And in general, it seems that Arch is all aboard the systemd train. Generally new versions hit the official repos relatively quickly (still waiting for 233, though), and the wiki has a ton of great systemd resources. systemd-nspawn is the preferred way to launch a container. In general, it feels like any problem you mention, the solution is given with systemd.

mkinitcpio hooks

A notable place where Arch has not yet embraced systemd by default is in the initramfs. The default configuration uses busybox, and includes the individual udev, usr, and resume hooks instead of the systemd hook, as well as keymap and console instead of systemd-vconsole, and similar for encrypt and lvm. There may be technical reasons for why the default configuration has not changed, but I have not run into any issues switching to the systemd versions of the hooks and I haven't looked into to the reasons for Arch not switching.

Personally, my motivation to switch was driven by a desire for more detailed profiling in the systemd-analyze tool. If booting a system without systemd-boot or the systemd hooks, systemd-analyze will show you two categories: kernel and user. Kernel will include the time for the kernel as well as the initrd. Using the systemd initramfs hooks adds a specific entry for the initrd.

Switching to the systemd hooks isn't particularly painful, but you will want to do three things before doing so. The first is make a backup of /boot/initramfs-linux-fallback.img. Copy it to to something like /boot/initramfs-linux-fallback-backup.img. Second, whatever bootloader you use, whether it's rEFInd, systemd-boot, or GRUB, make sure that you have it configured to allow you to modify boot entries and know how to get to that point. And finally, copy /etc/mkinitcpio.conf to /etc/mkinitcpio.bak.

One important thing I didn't realize that I wish I had was that the base hook provides a busybox recovery shell. This would have been useful as I was troubleshooting, but at this point I only add it in if I will be modifying my mkinitcpio configs or the kernel.

I then went into /etc/mkinitcpio.conf and was able to slim

HOOKS="base udev autodetect modconf block filesystems keyboard consolefont fsck"

All the way down to

HOOKS="systemd autodetect modconf block filesystems"

While I was there, I also switched the compression algorithm to LZ4.Then I ran # mkinitcpio -p linux and rebooted. And I had kernel, initrd, and user sections in my systemd-analyze output. I really wanted to go further, though.

systemd-boot

While there are more beautiful EFI bootloaders like rEFInd or older ones like GRUB, I have come to really like using systemd-boot. The configuration is easy to adjust, it automatically detects Windows and macOS and can boot those, and it adds two more sections to the systemd-analyze output: firmware and loader

Switching to systemd-boot actually took some work because previously, I had used my EFI System Partition (esp) solely for EFI programs and tools with kernels and my initramfs located in /boot on my rootfs. To make the change, I copied all non-rEFInd things in /boot to /boot/EFI, where my esp was mounted. Then I unmounted the esp, removed everything in /boot, and mounted the esp to boot (while also updating my fstab). I found all rEFInd-related things on the esp and removed those and then used bootctl to install systemd-boot to /boot.

At this point, I had to configure systemd-boot, which proved to be a lot easier than I expected. In /boot/loader/loader.conf I set my default boot option, and set timeout and editor to 0. Then in /boot/loader/entries/ I made an entry for each Linux kernel I wanted to use, added an extra initrd line for the Intel Microcode updates, and added my options.

After a reboot, my system came up happily. Now I have all the information from systemd-analyze that I could ever want.

Startup finished in 4.322s (firmware) + 57ms (loader) + 995ms (kernel) + 875ms (initrd) + 1.802s (userspace) = 8.053s