Initramfs with systemd & LUKS

TL;DR

[me@mycomputer]# cat /etc/sbupdate.conf | grep "^CMDLINE_DEFAULT"
CMDLINE_DEFAULT="rd.luks.uuid=c1f995f5-a8f7-47f0-b085-6d3a159e1874 rd.luks.allow-discards resume=UUID=51384ac6-f197-41d9-b8c8-c9607d7e01c8 rd.udev.log-priority=3 nvme.noacpi=1 quiet splash root=UUID=a645810c-ef87-4a9a-9239-afdeaf292e6e rw"

[me@mycomputer]# cat /etc/mkinitcpio.conf | grep "^HOOKS"
HOOKS=(systemd keyboard autodetect sd-vconsole modconf block sd-encrypt lvm2 filesystems fsck)

My old boot process looks like this:

  1. UEFI (with secure boot on)
  2. systemd-boot
  3. unified kernel.efi (initramfs + kernel params + kernel all rolled into one efi and signed)
  4. initramfs (busybox)
  5. encrypt hook: detects that a password is needed -> prompt for password
  6. Unlock LUKS partition
  7. LVM hook detects the LUKS partition and loads the logical volume groups/volumes
  8. The root partition is loaded and control gets passed off to the real kernel

Which was set up via the following configs:

[me@mycomputer]# parted --list
Model: SHPP41-2000GM (nvme)
Disk /dev/nvme0n1: 2000GB
Sector size (logical/physical): 4096B/4096B
Partition Table: gpt
Disk Flags: 

Number  Start   End     Size    File system  Name   Flags
 1      1049kB  1074MB  1073MB  fat32        boot   boot, esp
 2      1074MB  2000GB  1999GB               luksy

[me@mycomputer]# /dev/mapper/luksyvg-swap: UUID="51384ac6-f197-41d9-b8c8-c9607d7e01c8" TYPE="swap"
/dev/nvme0n1p1: UUID="00BA-48B3" BLOCK_SIZE="4096" TYPE="vfat" PARTLABEL="boot" PARTUUID="b6ed23fd-986e-4976-a499-410fbeff438e"
/dev/nvme0n1p2: UUID="c1f995f5-a8f7-47f0-b085-6d3a159e1874" TYPE="crypto_LUKS" PARTLABEL="luksy" PARTUUID="62278a82-aa36-4ead-afd7-86eebd1d8ba8"
/dev/mapper/luksyvg-root: UUID="a645810c-ef87-4a9a-9239-afdeaf292e6e" BLOCK_SIZE="4096" TYPE="ext4"
/dev/mapper/luks-c1f995f5-a8f7-47f0-b085-6d3a159e1874: UUID="JtyrtA-F0ee-roqi-tVdN-KRly-l4Oo-kHwHfo" TYPE="LVM2_member"
/dev/mapper/luksyvg-home: LABEL="home" UUID="394fa3e0-4539-43ca-a7ca-66158c2156f8" UUID_SUB="cf062892-876f-448e-b030-3d47dc5a7419" BLOCK_SIZE="4096" TYPE="btrfs"

[me@mycomputer]# cat /etc/sbupdate.conf | grep "^CMDLINE_DEFAULT"
CMDLINE_DEFAULT="cryptdevice=UUID=c1f995f5-a8f7-47f0-b085-6d3a159e1874:luksy root=/dev/luksyvg/root rw resume=UUID=51384ac6-f197-41d9-b8c8-c9607d7e01c8 quiet splash rd.udev.log-priority=3 nvme.noacpi=1"

[me@mycomputer]# cat /etc/mkinitcpio.conf | grep "^HOOKS"
HOOKS=(base udev autodetect keyboard keymap modconf block encrypt lvm2 filesystems resume fsck)

The main thing here is the addition of cryptdevice for the command line flags which triggers the code to unlock /dev/nvme0n1p2

I wanted to switch steps 4-8 to use systemd, rather than busybox. Arch has a decent amount of info about configuring mkinitcpio.conf, however it wasn’t clear to me exactly what to do for LUKS encryption. Should I mess with /etc/crypttab.initramfs? What should the kernel options be? After a lot of searching, I eventually figured out a setup for my system from the combination of dm_crypt wiki docs, dracut man pages, and a forum post (self-answered at that) about setting up resume

Once you know what to do, it’s pretty easy, but the key is knowing what to do :). Let’s dig in:

Modify mkinitcpio.conf to switch to systemd

This is pretty straightforward. Just follow the wiki and replace hooks with their corresponding systemd ones. My new HOOKS section looks like this:

HOOKS=(systemd keyboard autodetect sd-vconsole modconf block sd-encrypt lvm2 filesystems fsck)

Figure out your commandline parameters

This is the least-documented part. First, you need to tell systemd to unlock your file. You’ll need the UUID of the partition (e.g. /dev/nvme0n1p2 in my case), and then just add rd.luks.uuid=c1f995f5-a8f7-47f0-b085-6d3a159e1874 matching the UUID to your partition.

Next, especially when using LVM, I found it much simpler to just use the UUID for the root parameter to boot from. When booted into your current system, just use blkid to figure out the UUID of your /root LVM partition, then set root=UUID=a645810c-ef87-4a9a-9239-afdeaf292e6e rw as the corresponding command line argument (changing the UUID to match) Obviously this is going to be different than the UUID you have in rd.luks.uuid since that refers to your encrypted partition, and this value would be the sub-partition once you’ve unlocked it.

Getting hibernate to work

Once I had the above system, the only thing I needed to do for hibernation was to set resume=UUID=51384ac6-f197-41d9-b8c8-c9607d7e01c8 (this time matching the UUID to my swap partition)

I originally followed the steps from this forum post, and added the rd.lvm.lv flags. However, once I figured out my current setup, I realized it wasn’t needed for me. YMMV. I did learn about the rd.luks.options=discard from that post and added it to make sure my trim commands worked