Hey nice article! As the primary developer of systemd stage 1, I do have some notes
About debugging, it’s worth noting the kernel params rd.systemd.unit=
, and rd.systemd.debug_shell
. These are equivalent to their stage 2 equivalents (without the rd.
prefix) and in fact most of the systemd kernel params will work either directly or with the rd.
prefix to indicate it should only apply in stage 1. You can use rd.systemd.unit=rescue.target
to enter a rescue mode in stage 1 instead of booting immediately, though you’ll need to set boot.initrd.systemd.emergencyAccess
to be able to get to a shell with this. Or, rd.systemd.debug_shell
will start a shell on tty9 that you can switch to while stage 1 is proceeding normally.
Network config changes slightly with a systemd based initd. Network interfaces don’t automatically get set up based on your networking.interfaces.*
config
Actually they should, if you use boot.initrd.network.enable
(which implicitly enables boot.initrd.systemd.network.enable
). It’s kinda like the difference between networking.useNetworkd
and systemd.network.enable
. The systemd option just turns on networkd
, but the higher level option implements networking.*
options implicitly using networkd. Right now the focus was on matching the scripted stage 1’s networking implementation rather than stage 2’s networking implementation, so IIRC it merely configures the networking.interfaces.*
interfaces, but it also implements a few of the virtual interfaces like bridges I think.
but if there is some binary you require in your initrd environment, boot.initrd.systemd.storePaths
Try boot.initrd.systemd.initrdBin
or boot.initrd.systemd.extraBin
to get binaries on PATH
in the shell.
and I used boot.initrd.network.postCommands
to populate my initrd root user’s .profile
with commands to decrypt my ZFS encryption dataset, and kill the local decryption command (zfs load-key rpool/crypt; killall zfs
).
Rather than manually loading ZFS keys and killing ZFS processes, try systemd-tty-ask-password-agent
. The ZFS service that unlocks datasets uses systemd-ask-password
(and indeed so do other things like LUKS or bcachefs password prompts), so any ask-password agent is able to reply to this prompt. You can just run the tty agent in your shell to answer it. Or what I do actually is use systemctl default
, which just spawns the tty ask-password agent in the shell and waits for initrd.target
to be reached or failed.
The main downside of this is that ZFS native encryption doesn’t support key rotation without copying all of your data
zfs change-key
?
I want to try to use LUKS on a ZVol to store file encryption keys for my encrypted datasets instead
I actually do exactly this, but for a different reason. For one, I use it to have multiple key slots to unlock an encrypted ZFS dataset, which ZFS doesn’t have an equivalent of. But mainly, I use it for all of the extremely nice LUKS features that systemd has, like binding to the TPM2, or a FIDO2 key. FIDO2 is especially nice as a dramatically less complicated way to use yubikeys than what you have to do with scripted stage 1.
Thanks for writing this! Documentation about this migration is something I need to include in the NixOS manual sometime soon, and this provided me with some great notes on what to include.