Zum Inhalt

Arch Linux Bare-Metal Provisioning: LUKS2 Argon2id Encryption, Btrfs Subvolumes, Unified Kernel Images (UKI) & Secure Boot

Executive Summary: A standard unencrypted Linux installation leaves data at rest exposed to physical theft and lacks atomic rollback protection against broken kernel or rolling-release upgrades. This architectural installation guide walks through a bare-metal Arch Linux provisioning workflow utilizing GPT partitioning, LUKS2 (aes-xts-plain64 + Argon2id) full-disk encryption, Btrfs copy-on-write subvolumes (@, @home, @snapshots, @log), systemd-based initramfs (sd-encrypt), signed Unified Kernel Images (UKI) booted directly from UEFI firmware via sbctl Secure Boot, and automated snapper + snap-pac pre/post-Pacman snapshot hooks.

  • LUKS2 + Btrfs CoW Subvolumes


    Isolate /, /home, /var/log, and /.snapshots inside compressed (zstd:3) Btrfs subvolumes atop an Argon2id-hardened LUKS2 volume.

  • Unified Kernel Images (UKI) & TPM2


    Bundle the kernel, microcode, initramfs, and hardened kernel cmdline into a single PE binary (/efi/EFI/Linux/arch-linux.efi) verified by UEFI Secure Boot.

  • Transactional Pacman Rollbacks


    Trigger automatic read-only Btrfs snapshots before and after every pacman -Syu upgrade using snapper and snap-pac.

  • In-RAM Compressed Swap (zram)


    Eliminate physical swap partition wear on NVMe storage using zram-generator with zstd page compression.


1. Storage & Boot Chain Architecture

Rather than relying on a fragile multi-stage GRUB configuration with an unencrypted /boot partition, this architecture uses a 1 GiB UEFI System Partition (ESP) containing a single cryptographically signed Unified Kernel Image (UKI) that unlocks the LUKS2 root container via systemd's sd-encrypt hook:

%%{init: {'flowchart': {'wrappingWidth': 600, 'subGraphTitleMargin': {'top': 15, 'bottom': 15}}}}%%
flowchart TB
    subgraph Firmware["UEFI Firmware (Secure Boot)"]
        UEFI["Custom db/KEK/PK Keys<br/>Enrolled via sbctl"]
    end

    subgraph ESP["/dev/nvme0n1p1 (1 GiB FAT32 ESP)"]
        BootLoader["systemd-boot + Signed UKI<br/>arch-linux.efi (Kernel+Initrd)"]
    end

    subgraph LUKSContainer["/dev/nvme0n1p2 (LUKS2 Argon2id + AES-XTS-512)"]
        BtrfsPool["Btrfs Filesystem (zstd:3, noatime)"]
        SubRoot["Subvolume @ -> /"]
        SubHome["Subvolume @home -> /home"]
        SubSnap["Subvolume @snapshots -> /.snapshots"]
        SubLog["Subvolume @log -> /var/log"]
    end

    UEFI -->|"Verify PE Signature"| BootLoader
    BootLoader -->|"sd-encrypt (Passphrase / TPM2)"| BtrfsPool
    BtrfsPool --> SubRoot
    BtrfsPool --> SubHome
    BtrfsPool --> SubSnap
    BtrfsPool --> SubLog

2. Pre-Installation Verification, Networking & GPT Partitioning

Boot the latest Arch Linux ISO in 64-bit UEFI mode, verify Secure Boot setup mode, synchronize the system clock via NTP, and partition the target NVMe drive (/dev/nvme0n1):

Verifying UEFI Mode, Configuring Keymap & Partitioning NVMe via sgdisk
# 1. Verify 64-bit UEFI boot mode (Must return '64')
cat /sys/firmware/efi/fw_platform_size

# 2. Set German or US keyboard layout and enable NTP clock sync
loadkeys de-latin1
timedatectl set-ntp true

# 3. Wipe existing partition tables and create GPT layout:
#    Partition 1: 1 GiB EFI System Partition (ef00)
#    Partition 2: Remaining NVMe space for Linux LUKS2 (8309)
sgdisk --zap-all /dev/nvme0n1
sgdisk --clear \
  --new=1:0:+1G   --typecode=1:ef00 --change-name=1:"EFI" \
  --new=2:0:0     --typecode=2:8309 --change-name=2:"cryptsystem" \
  /dev/nvme0n1
partprobe /dev/nvme0n1

3. LUKS2 Cryptographic Formatting & Btrfs Subvolume Topology

Format /dev/nvme0n1p2 using LUKS2 with aes-xts-plain64 (512-bit key = 256-bit AES + 256-bit XTS tweak), argon2id memory-hard PBKDF, and 4,096-byte hardware sectors for optimal NVMe performance. Enable --allow-discards so weekly fstrim.timer passes TRIM commands down to the NVMe controller.

Formatting LUKS2 Container & Creating Btrfs Subvolumes
# 1. Format EFI System Partition as FAT32
mkfs.fat -F 32 -n EFI /dev/nvme0n1p1

# 2. Initialize LUKS2 volume with Argon2id and 4K sector alignment
cryptsetup luksFormat \
  --type luks2 \
  --cipher aes-xts-plain64 \
  --key-size 512 \
  --hash sha512 \
  --pbkdf argon2id \
  --iter-time 4000 \
  --sector-size 4096 \
  /dev/nvme0n1p2

# 3. Open LUKS2 container with persistent TRIM/discard and no-read/write-workqueue flags
cryptsetup --allow-discards --perf-no_read_workqueue --perf-no_write_workqueue --persistent \
  open /dev/nvme0n1p2 cryptroot

# 4. Format Btrfs and create subvolumes so @snapshots, @log, and @pkg are excluded from root rollbacks
mkfs.btrfs -L ARCH /dev/mapper/cryptroot
mount /dev/mapper/cryptroot /mnt

btrfs subvolume create /mnt/@
btrfs subvolume create /mnt/@home
btrfs subvolume create /mnt/@snapshots
btrfs subvolume create /mnt/@log
btrfs subvolume create /mnt/@pkg
umount /mnt

# 5. Mount subvolumes with zstd:3 compression and SSD space_cache=v2
BTRFS_OPTS="noatime,compress=zstd:3,ssd,discard=async,space_cache=v2"
mount -o "${BTRFS_OPTS},subvol=@" /dev/mapper/cryptroot /mnt
mkdir -p /mnt/{efi,home,.snapshots,var/log,var/cache/pacman/pkg}

mount -o "${BTRFS_OPTS},subvol=@home"      /dev/mapper/cryptroot /mnt/home
mount -o "${BTRFS_OPTS},subvol=@snapshots" /dev/mapper/cryptroot /mnt/.snapshots
mount -o "${BTRFS_OPTS},subvol=@log"       /dev/mapper/cryptroot /mnt/var/log
mount -o "${BTRFS_OPTS},subvol=@pkg"       /dev/mapper/cryptroot /mnt/var/cache/pacman/pkg
mount -o umask=0077 /dev/nvme0n1p1 /mnt/efi

4. Base System Bootstrap (pacstrap), Chroot & Systemd Initramfs

Install the LTS and mainline kernels, CPU microcode (amd-ucode or intel-ucode), Btrfs tools, NetworkManager, and Secure Boot utilities (sbctl), then generate /etc/fstab and enter arch-chroot:

Bootstrapping Base System & Generating /etc/fstab
pacstrap -K /mnt base base-devel linux linux-lts linux-firmware \
  amd-ucode btrfs-progs cryptsetup zram-generator networkmanager \
  neovim git sudo sbctl snapper snap-pac man-db

genfstab -U /mnt >> /mnt/etc/fstab
arch-chroot /mnt

Inside the arch-chroot environment, configure localization, /etc/kernel/cmdline, and mkinitcpio.conf using systemd-native hooks (systemd, sd-vconsole, sd-encrypt) to generate a Unified Kernel Image (UKI) directly into /efi/EFI/Linux/:

Configuring Locale, Kernel Cmdline & Unified Kernel Image (UKI) inside Chroot
# 1. Timezone, Locale, and Hostname
ln -sf /usr/share/zoneinfo/Europe/Berlin /etc/localtime
hwclock --systohc
sed -i 's/^#en_US.UTF-8 UTF-8/en_US.UTF-8 UTF-8/' /etc/locale.gen
locale-gen
echo "LANG=en_US.UTF-8" > /etc/locale.conf
echo "KEYMAP=de-latin1" > /etc/vconsole.conf
echo "arch-workstation" > /etc/hostname

# 2. Configure Kernel Command Line with LUKS2 UUID and Btrfs root subvolume
LUKS_UUID=$(blkid -s UUID -o value /dev/nvme0n1p2)
cat << EOF > /etc/kernel/cmdline
rd.luks.name=${LUKS_UUID}=cryptroot rd.luks.options=discard root=/dev/mapper/cryptroot rootflags=subvol=@ rw quiet loglevel=3
EOF

# 3. Configure systemd-based mkinitcpio hooks
sed -i 's/^HOOKS=.*/HOOKS=(base systemd autodetect microcode modconf kms keyboard sd-vconsole block sd-encrypt filesystems fsck)/' /etc/mkinitcpio.conf

# 4. Enable UKI generation in /etc/mkinitcpio.d/linux.preset
mkdir -p /efi/EFI/Linux
cat << 'EOF' > /etc/mkinitcpio.d/linux.preset
ALL_kver="/boot/vmlinuz-linux"
PRESETS=('default')
default_uki="/efi/EFI/Linux/arch-linux.efi"
EOF

mkinitcpio -P

5. Secure Boot Key Enrollment (sbctl), TPM2 Unlock & Zram Swap

Install systemd-boot to the ESP, generate and enroll custom UEFI Secure Boot keys (including Microsoft's vendor UEFI ROM keys via -m so discrete GPU Option ROMs still initialize), and configure zram-generator:

Enrolling Custom UEFI Secure Boot Keys & Configuring Zram Swap
# 1. Install systemd-boot to /efi
bootctl install

# 2. Create, enroll, and sign UEFI binaries with sbctl (Reboot into UEFI Setup Mode first if needed)
sbctl create-keys
sbctl enroll-keys -m
sbctl sign -s /efi/EFI/BOOT/BOOTX64.EFI
sbctl sign -s /efi/EFI/systemd/systemd-bootx64.efi
sbctl sign -s /efi/EFI/Linux/arch-linux.efi

# 3. Configure compressed in-RAM Zram swap (/etc/systemd/zram-generator.conf)
cat << 'EOF' > /etc/systemd/zram-generator.conf
[zram0]
zram-size = min(ram / 2, 8192)
compression-algorithm = zstd
EOF

# 4. Enable essential services
systemctl enable NetworkManager.service fstrim.timer

6. Post-Boot Verification & TPM2 + Snapper Checks

After rebooting into the newly provisioned Arch system and enabling Secure Boot in UEFI firmware, verify Secure Boot enforcement, optional TPM2 PCR7+15 binding, and snapper snapshots:

Verifying Secure Boot Status, Zram & Snapper Btrfs Snapshots
# 1. Verify Secure Boot is active and all UKI binaries are signed
sudo sbctl status
sudo sbctl verify

# 2. Optional: Bind LUKS2 unlock to TPM2 PCR 7 (Secure Boot state) + PIN
sudo systemd-cryptenroll --tpm2-device=auto --tpm2-pcrs=7 --tpm2-with-pin=yes /dev/nvme0n1p2

# 3. Verify active Zram swap device and Btrfs subvolume mounts
zramctl
btrfs subvolume list /