Blog
Install Arch with encrypted root + swap and UKI
Table of contents
I wanted an Arch setup where both root and swap stay encrypted, resume works, and systemd-boot loads a unified kernel image (UKI). This is the exact flow I used on a blank NVMe drive.
Layout used below:
/dev/nvme0n1p1(EFI, 512M),/dev/nvme0n1p2(LUKS swap),/dev/nvme0n1p3(LUKS root, Btrfs). Swap lives on its own LUKS volume so hibernation resumes from encrypted space.1
Partition, encrypt, and mount
Create three GPT partitions with cfdisk or parted. For the EFI slice, set the partition type to EFI System (GUID type EF00) and toggle the esp/boot flag; formatting alone is not enough.2 Then format it:
mkfs.fat -F32 -n EFI /dev/nvme0n1p1
Encrypt swap and root, then open them:3
cryptsetup luksFormat /dev/nvme0n1p2 # swap
cryptsetup luksFormat /dev/nvme0n1p3 # root
cryptsetup open /dev/nvme0n1p2 cryptswap
cryptsetup open /dev/nvme0n1p3 cryptroot
Remember that the password for root and swap must be same especially if you want to use hibernate.
Format and mount:
mkswap /dev/mapper/cryptswap
swapon /dev/mapper/cryptswap
mkfs.btrfs /dev/mapper/cryptroot
mount /dev/mapper/cryptroot /mnt
btrfs subvolume create /mnt/@
btrfs subvolume create /mnt/@home
umount /mnt
mount -o noatime,ssd,compress=zstd,space_cache=v2,discard=async,subvol=@ /dev/mapper/cryptroot /mnt
mkdir -p /mnt/{home,boot,efi}
mount -o noatime,ssd,compress=zstd,space_cache=v2,discard=async,subvol=@home /dev/mapper/cryptroot /mnt/home
mount /dev/nvme0n1p1 /mnt/efi
Base system
pacstrap -K /mnt base base-devel linux linux-firmware btrfs-progs sudo vim networkmanager plymouth
genfstab -U /mnt >> /mnt/etc/fstab
arch-chroot /mnt
Drop plymouth if you don't want a splash
Inside the chroot, set time, locale, hostname, and users:
ln -sf /usr/share/zoneinfo/Region/City /etc/localtime
hwclock --systohc
echo "en_US.UTF-8 UTF-8" >> /etc/locale.gen
locale-gen
echo "LANG=en_US.UTF-8" > /etc/locale.conf
echo "nexus" > /etc/hostname
passwd
useradd -m -G wheel xero && passwd xero
EDITOR=vim visudo # enable wheel sudo
systemctl enable NetworkManager
systemd-boot and UKI
Install systemd-boot to the EFI partition we mounted at /efi:
bootctl install --esp-path=/efi
Use systemd-style hooks for mkinitcpio so the initramfs aligns with UKI boot:
sed -i 's/^HOOKS=.*/HOOKS=(base systemd plymouth autodetect microcode modconf kms keyboard keymap sd-vconsole block sd-encrypt filesystems fsck)/' /etc/mkinitcpio.conf
Again drop plymouth if you don't want splash screen
Preset to emit a UKI (edit /etc/mkinitcpio.d/linux.preset):
# /etc/mkinitcpio.d/linux.preset
ALL_kver="/boot/vmlinuz-linux"
PRESETS=('default')
default_image="/boot/initramfs-linux.img"
default_uki="/efi/EFI/Linux/arch-linux.efi"
Write the kernel command line to /etc/kernel/cmdline with your own UUIDs from blkid:4
rd.luks.name=root-uuid=cryptroot root=/dev/mapper/cryptroot rd.luks.name=swap-uuid=cryptswap rootflags=subvol=@ rw resume=/dev/mapper/cryptswap
Plymouth splash (optional but works with UKI)
Inside the chroot, add quiet splash to /etc/kernel/cmdline next to your LUKS/resume args. The hook order above already puts plymouth before sd-encrypt; just regenerate initramfs when you change the cmdline:
mkinitcpio -P
bootctl update
Zswap (optional)
If RAM is tight or you want compressed swap, add these to /etc/kernel/cmdline on the same line as the LUKS/resume args (spaces between each item):
zswap.enabled=1 zswap.shrinker_enabled=1 zswap.compressor=lz4 zswap.max_pool_percent=30 zswap.zpool=zsmalloc rd.luks.name=swap-uuid=cryptswap
Build and keep entries fresh:
mkinitcpio -P
bootctl update
systemctl enable systemd-boot-update.service
Verify and reboot
bootctl listshows the UKI entry (and kernel version) you expect./efi/EFI/Linux/arch-linux.efiexists with a fresh timestamp.- Reboot: enter the LUKS passphrase, watch Plymouth if enabled, land on
@, and check that hibernate/resume works from the encrypted swap by using this command:
sudo systemctl hibernate
Footnotes
-
Arch Wiki: EFI system partition ↩
-
Arch Wiki: Dm-crypt/Encrypting an entire system ↩
-
Arch Wiki: Power management/Suspend and hibernate ↩