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 viasbctlSecure Boot, and automatedsnapper+snap-pacpre/post-Pacman snapshot hooks.
-
LUKS2 + Btrfs CoW Subvolumes
Isolate
/,/home,/var/log, and/.snapshotsinside 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 -Syuupgrade usingsnapperandsnap-pac. -
In-RAM Compressed Swap (
zram)
Eliminate physical swap partition wear on NVMe storage using
zram-generatorwithzstdpage 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):
# 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.
# 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:
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/:
# 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:
# 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:
# 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 /