8.3 KiB
Systemd initramfs
The initial ramdisk is in essence a very small environment (early userspace)
whihc loads various kernel modules and sets up necessary things before handing
control over to init
program (systemd).
By default, Arch Linux uses a BusyBox+udev based initial ramdisk, generated by
mkinitcpio
. This default initrd is essentially just a small script, that
executes other scripts, called hooks.
As an alternative to this, it's possible to have systemd run from the very start, during that initial ramdisk phase. With this approach, the tasks ran at this phase are determined by regular systemd unit files.
Why?
Obviously, BusyBox initramfs works just fine, so why would you want to switch? Well, there's a few reasons:
- Consistency across boot phases: The same systemd process that handles your system after boot can also manage the early userspace during boot, providing consistency in handling services, devices and dependencies throughout the entire boot process.
- Simplified troubleshooting: The tools and logs available during the boot
process will be the same as those used once the system is fully booted,
allowing you to troubleshoot problems with familiar tools (
journalctl
,systemctl
, ...) - Consistent Unit Files: Since systemd uses the same unit files in the initramfs as it does in the fully booted system, the configuration for many tasks (like mounting filesystems) is unified, reducing duplication of configuration files.
- TPM Unlocking Support: Systemd has built-in support for requesting data from TPM, allowing for a setup with TPM auto-unlocking an encrypted root partition, without having to specify the decryption password.
- Parallel Service Startup: Systemd is known for the ability to start services in parallel, which can potentially speed up the boot process compared to sequential script-based approach.
- Integrated Mount Handling: With systemd, managing complex mount setup (e.g. LVM RAID) can be more seamless, since it natively supports these and can handle them with less custom scripting.
That said, it's important to also mention some downsides and reasons why you might not want to use systemd-based initramfs:
- Simplicity: If you prefer a simple, more minimalistic approach, BusyBox-based initramfs might be sufficient and easier to manage.
- Size: A systemd-based initramfs might be larger than a minimal BusyBox-based initramfs, which could be a concern on systems with very limited space.
- Compatibility: If you're running some custom scripts or hooks, they might not work with a systemd-based initramfs.
Switching to systemd initramfs
Open /etc/mkinitcpio.conf
and find a line that starts with HOOKS=
- Change
udev
tosystemd
- Change
keymap consolefont
tosd-vconsole
- Add
sd-encrypt
beforeblock
, and removeencrypt
- If you were using
mkinitcpio-numlock
, also removenumlock
, it doesn't work with systemd (we'll go over how to auto-enable numlock later)
Additionally, with systemd initramfs, you shouldn't be specifying root
nor
cryptdevice
kernel arguments, as systemd can actually pick those up
automatically (they'll be discovered by systemd-cryptsetup-generator and
auto-mounted from initramfs via systemd-gpt-auto-generator). We will however
still need the rootflags
argument for selecting the btrfs subvolume (unless
your default subvolume is the root partition subvolume).
So, let's edit our kernel parameters:
echo "rw loglevel=3" > /etc/kernel/cmdline # overwrite the existing cmdline
echo "rootflags=subvol=/@" >> /etc/kernel/cmdline
You'll also need to modify the /etc/fstab
, as systemd will not use the
/dev/mapper/cryptfs
name, but rather you'll have a /dev/gpt-auto-root
(there'll also be /dev/gpt-auto-root-luks
, which is the encrypted partition).
If you prefer using a mapper device, you can also use /dev/mapper/root
.
Alternatively, you can use the label to mount. (if you followed the
installation guide, that would be /dev/disk/by-label/FS
.)
vim /etc/fstab
Finally, regenerate the initramfs with: pacman -S linux
(you could also do
mkinitcpio -P
, however that won't trigger the pacman hook which auto-signs our
UKI images for secure boot, so you'd have to re-sign them with sbctl
manually,
if you're using secure-boot) and reboot to check if it worked.
Activating numlock
Since we had to remove mkinitcpio-numlock
, as that hook isintended for BusyBox
based initrd, we'll want to have an alternative available.
First though, we should also remove the package: pacman -R mkinitcpio-numlock
.
The simple, but imperfect option
There is a systemd-numlockontty
AUR package which creates a systemd service
that enables numlock in TTYs after booting (you'll need to enable it), this
however doesn't happen in initramfs directly, only afterwards.
Depending on what you will need, this may be sufficient. If you are going to be typing a decryption password at this early stage and you wish to have numlock support there, you will need to do some more work.
The proper solution
To enable numlock before you're prompted for the decryption password, we'll need
to create a custom initcpio hook, that will return a systemd service which will
do the enabling. We'll put this hook into /usr/lib/initcpio/install/numlock
,
with the following content:
#!/bin/bash
build() {
add_binary /bin/bash
add_binary /usr/bin/setleds
add_binary /usr/local/bin/numlock
cat >"$BUILDROOT/usr/lib/systemd/system/numlock.service" <<EOF
[Unit]
Description=Enable numlock
Before=cryptsetup-pre.target
DefaultDependencies=no
[Service]
Type=oneshot
RemainAfterExit=yes
ExecStart=/usr/local/bin/numlock
EOF
add_systemd_unit cryptsetup-pre.target
cd "$BUILDROOT/usr/lib/systemd/system/sysinit.target.wants" || exit
ln -sf /usr/lib/systemd/system/cryptsetup-pre.target cryptsetup-pre.target
ln -sf /usr/lib/systemd/system/numlock.service numlock.service
}
help() {
cat <<EOF
This hook adds support to enable numlock before sd-encrypt hook is run.
EOF
}
This script is also present in my dotfiles, so you can just copy it from there:
cp ~/dots/root/usr/lib/initcpio/install/numlock /usr/lib/initcpio/install
Next we will need to create that /usr/local/bin/numlock
script. This script
will do the actual enabling of numlock. Note that we can only use the binaries
that we explicitly included in our hook inside our script.
#!/bin/bash
for tty in /dev/tty[0-9]; do
/usr/bin/setleds -D +num < "$tty"
done
If you ran the install_root.sh
script from my dotfiles during
INSTALLATION, this script will already be present in
your /usr/local/bin
Now we will need to add our custom new numlock
hook to
/etc/mkinitcpio.conf
, before the sd-encrypt
hook (assuming you're using
encryption), but after the keyboard
and sd-vconsole
hooks.
Finally, we'll need to rebuild initramfs, which we should trigger with sudo pacman -S linux
, to make sure the secure-boot signing also runs. When
re-building the initramfs, pay attention on the output, you should see it pass
with no errors:
-> Running build hook: [base]
-> Running build hook: [systemd]
-> Running build hook: [autodetect]
-> Running build hook: [microcode]
-> Running build hook: [modconf]
-> Running build hook: [kms]
-> Running build hook: [keyboard]
-> Running build hook: [sd-vconsole]
-> Running build hook: [numlock] # <-- make sure this is present
-> Running build hook: [sd-encrypt]
-> Running build hook: [block]
-> Running build hook: [filesystems]
-> Running build hook: [fsck]
Note
If you see some warnings there, like:
==> WARNING: Possibly missing firmware for module: 'xyz'
, you can usually safely ignore these. Just make sure there's no==> ERROR: ...
If you didn't see any errors, you can now reboot.
Important
In some cases, the numlock led indicator might not turn on immediately, even though numlock was actually turned on. This may mislead you towards thinking it is not on, even though it actually is. I'd recommend trying it out by actually typing something it at this time.
Note that after this early boot stage, the indicator should light up eventually.