Arch Linux Secure Boot + Systemd-boot + Btrfs + Full Disk Encryption + TPM Auto-Unlock

This article will explain how to set up Secure Boot, Btrfs, full disk encryption, and TPM auto-unlock for Arch Linux, including systemd-boot and Unified Kernel Image (UKI).

AI Translation

Original Chinese

This article aims to install a relatively secure Arch Linux. It will also explain why these technologies are used, so you understand not only what but also why.

It must be stated in advance that this article targets Linux users with some foundation; it does not cover Windows users. Windows users are better off using simpler installation scripts like archinstall.

I also recommend users switching from Windows to Linux to first try a non-encrypted system or choose a more user-friendly Linux distributionFor example: openSUSE, which is quite friendly to beginners.,
to avoid early frustration. Experience the Linux ecosystem first, then consider the installation process described here. This article also does not include instructions on installing Arch Linux alongside Windows as dual-boot.

Philosophy

Full Disk Encryption (LUKS)

In this article, we will Full Disk Encryption (FDE). When powered off, our hard drive will be completely encrypted.
This means attackers cannot read any useful data from the drive. This effectively prevents, but is not limited to: loss or resale of the computer, and malicious forensic analysis1.

We will use LUKS to encrypt the root directory. This encryption is transparentmeaning the system is unaware the disk is encrypted; it only sees decrypted data and thus does not need to perform decryption itself..

systemd-boot

We will choose systemd-boot as our bootloader2. Compared to GRUB, it is faster, simpler, cleaner, and more actively maintained.
Although there is a trade-offWith GRUB, tools like grub-btrfs allow quick selection of past snapshots for rollback, though it’s not as convenient as openSUSE’s method. Arch’s rollback mechanism is more complex..

UKI

To improve security and simplify installation, we will use the Unified Kernel Image (UKI) for booting.
systemd-boot can automatically load UKI without extra configuration. This allows us to use what Arch calls the esp partition formatWe partition the disk into /efi and root..
We will mount only the esp directory, not the entire /boot partition, avoiding the complexity of encrypting the bootloader. The only unencrypted partition is esp. But you don’t need to worry about leaking information.
The esp boot partition contains only the UKI boot image, which holds no sensitive data and is signed later. 3

Secure Boot

Secure Boot is a complex concept. We only need to understand its key points. 2

When Secure Boot is enabled, at power-on, BIOS reads the Platform KeyYou can simply think of it as a key for signing and verifying the kernel. stored in BIOS to verify the kernel signature before booting.
If the kernel is unsigned or the signature is invalid, BIOS will refuse to boot the kerneli.e., the signed UKI mentioned above..
This prevents malicious tampering with the boot kernel to steal data.

Because Arch Linux does not sign kernels like openSUSE or Fedora, Secure Boot is typically disabled. However, we can self-sign the kernel, which can even be more secure.
Using the factory-installed platform key may risk leakage or malicious collusion, allowing attackers to sign malicious kernels and boot systems with them to steal information.

Btrfs

We use Btrfs as the root filesystem, allowing more flexible partitioning and removing concerns about partition sizes, avoiding problems of partition space shortageBtrfs treats the partition as a whole and uses subvolumes as logical partitions. All subvolumes share the space, so no shortage occurs..
Additionally, Btrfs supports snapshots, enabling tools like snapper or timeshift for convenientthough not perfectly convenient. snapshots and rollback. 4

tpm2

If your hardware lacks TPMMost CPUs now include it in firmware. Side note: When Microsoft promoted Windows 11, it required TPM, which was controversially interpreted as a ploy to force hardware upgrades or add backdoors. My view is that many users underestimate information security risks, ignoring real threats, which leads to many self-made insecure solutions. Honestly, security protocols or algorithms should always be publicly vetted models absolutely NO homegrown solutions!.

Using TPM lets you skip entering the disk password at boot. The password is handled by the TPM module. Simply put: when the system boots and needs the disk password,
it asks the TPM whether it can unlock the disk5. If TPM determines the environment is secure and kernel config unchanged, it grants unlockThis is a simplified explanation; the real process is more complex and not fully understood by me..

Some users worry that TPM auto-unlock poses security risks, e.g., if someone finds your lost laptop, it unlocks automatically on power-on. This is true; TPM protects only against disk theft, not whole machine theft.
TPM does not know who pressed the power button. After boot, the disk decrypts automatically; system security then depends on your user password. TPM’s purpose is to simplify repeated disk password entry, i.e., hardware unlock, with software-level protection via other means.

TPM protection is based on measuring hardware and software states6, like the upcoming 0+7This is a minimal measurement used only to enable TPM auto-unlock, not guaranteeing full security! You need to understand what you measure and test defenses against attacks. measurement.
This means TPM measures whether UEFI firmware has been altered or Secure Boot disabled, refusing to unlock if tampering is detected.

Here is what is protected in a simplified threat model:

  1. An attacker tries to steal the disk to read data. LUKS encryption prevents this by encrypting the disk.
  2. An attacker tries to load a malicious kernel to read the disk. Secure Boot blocks unsigned kernels.
  3. An attacker tries to modify boot parameters to steal the disk unlock key. TPM detects changes (PCR 8) and denies auto-unlock.
  4. An attacker tries a cold boot7 attack to extract keys from memory after TPM unlock. This attack succeeds; current schemes cannot prevent it.
    TPM stores keys in memory; cold boot can read this memory and extract keys. You can avoid this by not using TPM auto-unlock and entering the password manually at boot.
    The trade-off is entering the password every boot, or using full memory encryption8 to prevent this.

Timeshift

We choose Timeshift over snapper mainly because snapper’s configuration is complex, and systemd-boot doesn’t support boot-time snapshot selection.
Timeshift is more convenient. Our partition scheme also suits snapper with minimal adjustments.

Preparation Before Installation

After the above explanations, you should understand why we choose these technologies and potential attack surfaces. The key is to understand possible attacks and defend accordingly.
You cannot defend against every attack without making your system unusable or overly complex.

My computer is an ASUS TUF Gaming 4.

CPU GPU 0 GPU 1 SSD RAM
AMD R9 7940H AMD 780 M RTX 4060 512G 32G

Commands below are examples. For example, root@archiso ~ # sgdisk -n1:0:+512M -t1:ef00 -c1:EFI -N2 -t2:8304 -c2:LINUXROOT /dev/sda means the command is:
sgdisk -n1:0:+512M -t1:ef00 -c1:EFI -N2 -t2:8304 -c2:LINUXROOT /dev/sda, do not type the prompt root@archiso ~ #.

I will provide commands and their output separately. Do not repeat actions.

Download Image and Verify

Download the image from Arch Download. You may use magnet links or regional mirrors.

Verification is important to prevent tampered installs; instructions are on the website.

Flash the Image

Prepare a USB drive. You can use command-line tools or software like Impression.

Set Secure Boot to Setup Mode

The Arch installer cannot run with Secure Boot enabled, so disable Secure Boot. We will self-sign the kernel later, so setting Setup Mode is preferred.

Backup

danger

Back up before starting! Ensure your machine supports Platform Key removal; some devices may brick if keys are removed because certain hardware uses Microsoft’s platform key signature.

If you have an ASUS TUF Gaming 4 like me, enter BIOS and backup keys in Secure Boot settings.

Or use the efitools package to back up9.

efi-readvar -v PK -o old_PK.esl
efi-readvar -v KEK -o old_KEK.esl
efi-readvar -v db -o old_db.esl
efi-readvar -v dbx -o old_dbx.esl

Enter Setup Mode

Not sure why it’s called Setup Mode perhaps because you can set your own Platform Key? If your PC is like mine, enter BIOS, find Secure Boot, and choose to delete all keys to enter recovery mode. For other PCs, find your own way to enter Setup Mode.

Boot LiveOS

Connect to the network. Wifi is tricky; use a cable if possible. Ensure your PC boots in UEFI mode; BIOS mode is unsupported.

Now let’s start installation.

Check Your Disk

lsblk
root@archiso ~ # lsblk
NAME  MAJ:MIN RM   SIZE RO TYPE MOUNTPOINTS
loop0   7:0    0 813.4M  1 loop /run/archiso/airootfs
sda     8:0    0     8G  0 disk
sr0    11:0    1   1.1G  0 rom  /run/archiso/bootmnt

You can see three block devices: loop0, sda, and sr0. We only care about sda, the disk we will install to.

Set environment variable

export disk="/dev/sda"

Format Root Partition

Clear previous partition tables and info:

wipefs -af $disk

Create a new GPT partition table:

sgdisk --zap-all --clear $disk

Reload partition table:

partprobe $disk
root@archiso ~ # export disk="/dev/sda"
root@archiso ~ # wipefs -af $disk
root@archiso ~ # sgdisk --zap-all --clear $disk
Creating new GPT entries in memory.
GPT data structures destroyed! You may now partition the disk using fdisk or
other utilities.
The operation has completed successfully.
root@archiso ~ # partprobe $disk

(Optional) Fill Disk with Random Data

To clear potential data leaks, we use a fast filling method10.

Open a temporary encrypted device:

cryptsetup open --type plain -d /dev/urandom $disk target

Fill the encrypted device with zeros. Because it’s encrypted, writing zeros clears previous data.

dd if=/dev/zero of=/dev/mapper/target bs=1M status=progress oflag=direct

Close encrypted device:

cryptsetup close target
root@archiso ~ # cryptsetup open --type plain -d /dev/urandom $disk target
WARNING: Using default options for cipher (aes-xts-plain64, key size 256 bits) that could be incompatible with older versions.
For plain mode, always use options --cipher, --key-size and if no keyfile is used, then also --hash.
WARNING: Device /dev/sda already contains a 'gpt' partition signature.

WARNING!
========
Detected device signature(s) on /dev/sda. Proceeding further may damage existing data.

Are you sure? (Type 'yes' in capital letters): YES
root@archiso ~ # dd if=/dev/zero of=/dev/mapper/target bs=1M status=progress oflag=direct
8424259584 bytes (8.4 GB, 7.8 GiB) copied, 18 s, 468 MB/s
dd: error writing '/dev/mapper/target': No space left on device
8193+0 records in
8192+0 records out
8589934592 bytes (8.6 GB, 8.0 GiB) copied, 18.3393 s, 468 MB/s
1 root@archiso ~ # cryptsetup close target

Partitioning

We create two partitions: one ESP and one LUKS root11. No swap partition.

This is the ESP partition, 512 MiB for /efi, with partition type ef00.

sgdisk -n 0:0:+512MiB -t 0:ef00 -c 0:esp $disk

This is the LUKS2 partition occupying the rest of the disk, type 8309 (Linux LUKS).

sgdisk -n 0:0:0 -t 0:8309 -c 0:luks $disk

Reload partition table:

partprobe $disk
root@archiso ~ # sgdisk -n 0:0:+512MiB -t 0:ef00 -c 0:esp $disk
Creating new GPT entries in memory.
The operation has completed successfully.
root@archiso ~ # sgdisk -n 0:0:0 -t 0:8309 -c 0:luks $disk
The operation has completed successfully.
root@archiso ~ # partprobe $disk

Check partition table:

sgdisk -p $disk
root@archiso ~ # sgdisk -p $disk
Disk /dev/sda: 16777216 sectors, 8.0 GiB
Model: VBOX HARDDISK
Sector size (logical/physical): 512/512 bytes
Disk identifier (GUID): 9F19AB5D-08A8-40F7-9FAE-AE36F939123B
Partition table holds up to 128 entries
Main partition table begins at sector 2 and ends at sector 33
First usable sector is 34, last usable sector is 16777182
Partitions will be aligned on 2048-sector boundaries
Total free space is 2014 sectors (1007.0 KiB)

Number  Start (sector)    End (sector)  Size       Code  Name
   1            2048         1050623   512.0 MiB   EF00  esp
   2         1050624        16777182   7.5 GiB     8309  luks

Encryption

When using cryptsetup to encrypt the root partition, be sure to type the uppercase “YES” when prompted.

Also note that if your hardware uses an NVMe device, you need to change ${disk}2 to ${disk}p2. For example, /dev/sda2 or /dev/nvme0n1p2. You can check this with lsblk.

Here, the LUKS password is initially set to empty; we will remove this keyslot later. This is just for convenience during setup.

cryptsetup luksFormat --type luks2 ${disk}2
root@archiso ~ # cryptsetup luksFormat --type luks2 ${disk}2

WARNING!
========
This will overwrite data on /dev/sda2 irrevocably.

Are you sure? (Type 'yes' in capital letters): YES
Enter passphrase for /dev/sda2:
Verify passphrase:
cryptsetup luksFormat --type luks2 ${disk}2  15.59s user 0.33s system 151% cpu 10.487 total

Open the encrypted block device

cryptsetup open ${disk}2 linuxroot
root@archiso ~ # cryptsetup open ${disk}2 linuxroot
Enter passphrase for /dev/sda2:
cryptsetup open ${disk}2 linuxroot  6.92s user 0.05s system 229% cpu 3.032 total

Format file systems

Format the EFI partition:

mkfs.vfat -F32 -n EFI /dev/sda1
root@archiso ~ # mkfs.vfat -F32 -n EFI /dev/sda1
mkfs.fat 4.2 (2021-01-31)

Format the encrypted root device /dev/mapper/linuxroot as btrfs. Note this is the opened encrypted device from above.

mkfs.btrfs -f -L linuxroot /dev/mapper/linuxroot
root@archiso ~ # mkfs.btrfs -f -L linuxroot /dev/mapper/linuxroot
btrfs-progs v6.11
See https://btrfs.readthedocs.io for more information.

Performing full device TRIM /dev/mapper/linuxroot (7.48GiB) ...
NOTE: several default settings have changed in version 5.15, please make sure
      this does not affect your deployments:
      - DUP for metadata (-m dup)
      - enabled no-holes (-O no-holes)
      - enabled free-space-tree (-R free-space-tree)

Label:              linuxroot
UUID:               d6e9359c-b5e0-471b-9b6d-4d2c9e8f6caa
Node size:          16384
Sector size:        4096 (CPU page size: 4096)
Filesystem size:    7.48GiB
Block group profiles:
  Data:             single            8.00MiB
  Metadata:         DUP             256.00MiB
  System:           DUP               8.00MiB
SSD detected:       no
Zoned device:       no
Features:           extref, skinny-metadata, no-holes, free-space-tree
Checksum:           crc32c
Number of devices:  1
Devices:
   ID        SIZE  PATH
    1     7.48GiB  /dev/mapper/linuxroot

Create Btrfs subvolumes

Mount the root partition:

mount /dev/mapper/linuxroot /mnt

Create subvolumes. This layout is a basic example you might want a more detailed layout depending on your needs. This partitioning style is inspired by the archinstall script.

Subvolumes mainly help with backups using Timeshift, by separating directories that don’t need backup to avoid unnecessary snapshots.

Subvolume Mount point Description
@ / Root directory
@home /home Home directory; avoid losing important files during rollback
@cache /var/cache Cache files; no need to rollback
@log /var/log Log files; avoid rollback for easier debugging
@root /root Root user’s home directory, similar to /home
btrfs subvolume create /mnt/@
btrfs subvolume create /mnt/@home
btrfs subvolume create /mnt/@cache
btrfs subvolume create /mnt/@log
btrfs subvolume create /mnt/@root
root@archiso ~ # btrfs subvolume create /mnt/@
btrfs subvolume create /mnt/@home
btrfs subvolume create /mnt/@cache
btrfs subvolume create /mnt/@log
btrfs subvolume create /mnt/@root
Create subvolume '/mnt/@'
Create subvolume '/mnt/@home'
Create subvolume '/mnt/@cache'
Create subvolume '/mnt/@log'
Create subvolume '/mnt/@root'

Check subvolumes (IDs may differ, no problem):

btrfs subvolume list /mnt
root@archiso ~ # btrfs subvolume list /mnt
ID 256 gen 10 top level 5 path @
ID 257 gen 10 top level 5 path @home
ID 258 gen 10 top level 5 path @cache
ID 259 gen 10 top level 5 path @log
ID 260 gen 10 top level 5 path @root

Unmount root partition:

umount /mnt

Remount all partitions

The previous mount was only for creating subvolumes. Now mount the subvolumes properly.

Note the -t btrfs is just to specify the filesystem type and help shell completion. The subvol option specifies which subvolume to mount.

Compression is enabled with zstd, which saves space and can improve performance. The zstd:1 means compression level 1 (range 1-5, default 3). According to Arch Wiki, level 1 improves fragmentation and reduces IO, potentially improving performance. I haven’t benchmarked this myself, so take it with a grain of salt. But I follow the Wiki recommendation. See Btrfs wiki for more options and details.

mount -t btrfs -o subvol=@,compress=zstd:1 -m /dev/mapper/linuxroot /mnt
mount -t btrfs -o subvol=@home,compress=zstd:1 -m /dev/mapper/linuxroot /mnt/home
mount -t btrfs -o subvol=@cache,compress=zstd:1 -m /dev/mapper/linuxroot /mnt/var/cache
mount -t btrfs -o subvol=@log,compress=zstd:1 -m /dev/mapper/linuxroot /mnt/var/log
mount -t btrfs -o subvol=@root,compress=zstd:1 -m /dev/mapper/linuxroot /mnt/root

Mount EFI partition:

mount -m /dev/sda1 /mnt/efi
Deprecated Partition Layout (updated 2025-08-09)

The following layout is now outdated; you can refer to it for inspiration or customize it for your needs.

Compared with the current scheme, these subvolumes are no longer used: @var-tmp, @tmp, @libvirt, @docker, @srv, @usr-local, @containers.

Reasoning summarized below:

Subvolume Mount point Notes
@var-tmp /var/tmp Temporary files, usually small and auto-removed, so backup unnecessary
@tmp /tmp Usually mounted as tmpfs by systemd on Arch Linux, no need to manage here
@libvirt /var/lib/libvirt I don’t use virtual machines
@docker /var/lib/docker Same reason as above
@srv /srv No local web or FTP server deployment
@usr-local /usr/local Third-party software; might cause issues if not rolled back properly
@containers /var/lib/containers pacman work directory, similar to docker

Original layout commands:

btrfs subvolume create /mnt/@
btrfs subvolume create /mnt/@home
btrfs subvolume create /mnt/@var-tmp
btrfs subvolume create /mnt/@tmp
btrfs subvolume create /mnt/@cache
btrfs subvolume create /mnt/@log
btrfs subvolume create /mnt/@libvirt
btrfs subvolume create /mnt/@docker
btrfs subvolume create /mnt/@srv
btrfs subvolume create /mnt/@root
btrfs subvolume create /mnt/@usr-local
btrfs subvolume create /mnt/@containers

Check subvolumes list:

btrfs subvolume list /mnt

Unmount root:

umount /mnt

Remount all subvolumes:

mount -t btrfs -o subvol=@,compress=zstd:1 -m /dev/mapper/linuxroot /mnt
mount -t btrfs -o subvol=@home,compress=zstd:1 -m /dev/mapper/linuxroot /mnt/home
mount -t btrfs -o subvol=@var-tmp,compress=zstd:1 -m /dev/mapper/linuxroot /mnt/var/tmp
mount -t btrfs -o subvol=@tmp,compress=zstd:1 -m /dev/mapper/linuxroot /mnt/tmp
mount -t btrfs -o subvol=@cache,compress=zstd:1 -m /dev/mapper/linuxroot /mnt/var/cache
mount -t btrfs -o subvol=@log,compress=zstd:1 -m /dev/mapper/linuxroot /mnt/var/log
mount -t btrfs -o subvol=@libvirt,compress=zstd:1 -m /dev/mapper/linuxroot /mnt/var/lib/libvirt
mount -t btrfs -o subvol=@docker,compress=zstd:1 -m /dev/mapper/linuxroot /mnt/var/lib/docker
mount -t btrfs -o subvol=@root,compress=zstd:1 -m /dev/mapper/linuxroot /mnt/root
mount -t btrfs -o subvol=@usr-local,compress=zstd:1 -m /dev/mapper/linuxroot /mnt/usr/local
mount -t btrfs -o subvol=@containers,compress=zstd:1 -m /dev/mapper/linuxroot /mnt/var/lib/containers
mount -t btrfs -o subvol=@srv,compress=zstd:1 -m /dev/mapper/linuxroot /mnt/srv
Disabling Copy-on-Write (update: 2025-06-14)

According to reader feedback, it seems things have changed somewhat. As Btrfs becomes more popular, more well-designed software adapts to Btrfs, so manually disabling CoW seems somewhat unnecessary. Therefore, the steps below to disable CoW can be optionally skipped.

Note that disabling CoW will simultaneously disable Btrfs snapshotting, data checksumming, and compression. This is mainly to avoid frequent writes on directories like log, cache, tmp, and var/tmp which generally do not need snapshots. Also, docker, podman, and libvirt use their own image formats, and using CoW may cause performance issues.

chattr +C /mnt/var/log
chattr +C /mnt/var/cache
chattr +C /mnt/var/tmp
chattr +C /mnt/var/lib/docker
chattr +C /mnt/var/lib/libvirt
chattr +C /mnt/var/lib/containers
chattr +C /mnt/tmp

Currently, your partition setup should look like this:

root@archiso ~ # lsblk
NAME          MAJ:MIN RM   SIZE RO TYPE  MOUNTPOINTS
loop0           7:0    0 813.4M  1 loop  /run/archiso/airootfs
sda             8:0    0     8G  0 disk
├─sda1          8:1    0   512M  0 part  /mnt/efi
└─sda2          8:2    0   7.5G  0 part
  └─linuxroot 254:0    0   7.5G  0 crypt /mnt/srv
                                         /mnt/var/lib/containers
                                         /mnt/usr/local
                                         /mnt/root
                                         /mnt/var/lib/docker
                                         /mnt/var/lib/libvirt
                                         /mnt/var/log
                                         /mnt/var/cache
                                         /mnt/tmp
                                         /mnt/var/tmp
                                         /mnt/home
                                         /mnt
sr0            11:0    1   1.1G  0 rom   /run/archiso/bootmnt

Installing Base Packages

Before installation, use the reflector command to update mirror lists. Replace --country with your country or a nearby one.

reflector --country Japan --age 24 --protocol http,https --sort rate --save /etc/pacman.d/mirrorlist

The packages installed here mainly include the essential base system and some utilities. You can add or remove packages according to your needs.

Configuring pacman

Before installation, configure pacman to improve download speed and enable color output. Uncomment Color and ParallelDownloads = 5 in /etc/pacman.conf. This enables colored output and parallel downloads.

# Misc options
#UseSyslog
-#Color
+Color
#NoProgressBar
-#ParallelDownloads = 5
+ParallelDownloads = 5
DownloadUser = alpm
#DisableSandbox

Base Packages

Package Name Required Description
base Base system packages
base-devel Base development tools
linux Linux kernel
linux-firmware Linux firmware
btrfs-progs Btrfs support
util-linux Linux utilities, essential for system operation
cryptsetup Encryption/decryption tools used later
dosfstools FAT filesystem tools (e.g., mkfs.fat)
sbctl Secure Boot tool used later
vim / nano / neovim … Text editors; install at least one of your choice
amd-ucode / intel-ucode CPU microcode to fix CPU vulnerabilities; choose based on your CPU; skip in VMs
networkmanager Network manager; recommended for desktop environments
sudo Privilege escalation tool; essential to avoid system reinstall
unzip Unarchiving tool; install if needed
git Git version control, frequently used

Install with:

pacstrap -K /mnt base base-devel linux linux-firmware amd-ucode vim nano cryptsetup btrfs-progs dosfstools util-linux git unzip sbctl networkmanager sudo

Generate fstab file

This saves the mounts you painstakingly set up; otherwise they won’t persist after reboot.

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

Check to see if it looks correct, similar to this:

root@archiso ~ # cat /mnt/etc/fstab
# Static information about the filesystems.
# See fstab(5) for details.

# <file system> <dir> <type> <options> <dump> <pass>
# /dev/mapper/linuxroot LABEL=linuxroot
UUID=d6e9359c-b5e0-471b-9b6d-4d2c9e8f6caa /          btrfs      rw,relatime,compress=zstd:1,space_cache=v2,subvol=/@ 0 0

# /dev/mapper/linuxroot LABEL=linuxroot
UUID=d6e9359c-b5e0-471b-9b6d-4d2c9e8f6caa /home      btrfs      rw,relatime,compress=zstd:1,space_cache=v2,subvol=/@home 0 0

# /dev/mapper/linuxroot LABEL=linuxroot
UUID=d6e9359c-b5e0-471b-9b6d-4d2c9e8f6caa /var/cache btrfs      rw,relatime,compress=zstd:1,space_cache=v2,subvol=/@cache 0 0

# /dev/mapper/linuxroot LABEL=linuxroot
UUID=d6e9359c-b5e0-471b-9b6d-4d2c9e8f6caa /var/log   btrfs      rw,relatime,compress=zstd:1,space_cache=v2,subvol=/@log 0 0

# /dev/mapper/linuxroot LABEL=linuxroot
UUID=d6e9359c-b5e0-471b-9b6d-4d2c9e8f6caa /root      btrfs      rw,relatime,compress=zstd:1,space_cache=v2,subvol=/@root 0 0

# /dev/sda1 LABEL=EFI
UUID=AB14-F139       /efi       vfat       rw,relatime,fmask=0022,dmask=0022,codepage=437,iocharset=ascii,shortname=mixed,utf8,errors=remount-ro 0 2

Localization

Choose your locale. Here we select en_US.UTF-8. It’s best not to set the system language to Chinese or others at system level; you can configure Chinese in your desktop environment if needed.

sed -i -e "/^#en_US.UTF-8/s/^#//" /mnt/etc/locale.gen

Besides the above command, you can also open /mnt/etc/locale.gen with vim or nano and uncomment the line en_US.UTF-8 UTF-8.

Use this handy command to complete the remaining localization steps12.

Item Recommendation
keyboard layout (keymap) us
timezone If you are in mainland China, use Asia/Shanghai
hostname Choose what you like; here we pick arch-tmp
systemd-firstboot --root /mnt --prompt
root@archiso ~ # systemd-firstboot --root /mnt --prompt

Welcome to your new installation of Arch Linux!

Please configure your system!

-- Press any key to proceed --
 Please enter system keymap name or number (empty to skip, "list" to list options): us
/mnt/etc/vconsole.conf written.

 Please enter timezone name or number (empty to skip, "list" to list options): Asia/Shanghai
/mnt: /etc/localtime written

 Please enter hostname for new system (empty to skip): arch-tmp
/mnt: /etc/hostname written.

Generate locale:

arch-chroot /mnt locale-gen
root@archiso ~ # arch-chroot /mnt locale-gen
Generating locales...
  en_US.UTF-8... done
Generation complete.

Create /mnt/etc/locale.conf file with the language setting:

LANG=en_US.UTF-8

Add user account

Add a user; wheel is the user group. Here the username is admin, you can choose a different one.

arch-chroot /mnt useradd -G wheel -m admin

Set password for the user, don’t forget.

arch-chroot /mnt passwd admin
root@archiso ~ # arch-chroot /mnt useradd -G wheel -m admin
root@archiso ~ # arch-chroot /mnt passwd admin
New password:
Retype new password:
passwd: password updated successfully

Avoid using root as the primary user to prevent many issues.

Set admin privileges to allow the admin user to use sudo. You can escalate privileges via sudo.Do not edit sudoers directly; use /etc/sudoers.d/ for configuration to avoid misconfiguration and easier management per user.

echo "admin ALL=(ALL:ALL) ALL" >> /mnt/etc/sudoers.d/admin

Adjust cmdline

This allows systemd-boot to correctly boot the encrypted root filesystem. Incorrect configuration can cause failure to boot13.

Get sda UUID

Note the luks partition UUID /dev/sda2: UUID="3a29447e-599f-42fb-b3d2-6b44e871e8d6".

root@archiso ~ # blkid
/dev/sr0: BLOCK_SIZE="2048" UUID="2024-12-01-05-48-32-00" LABEL="ARCH_202412" TYPE="iso9660" PTUUID="382d5c21" PTTYPE="dos"
/dev/loop0: BLOCK_SIZE="1048576" TYPE="squashfs"
/dev/mapper/linuxroot: LABEL="linuxroot" UUID="d6e9359c-b5e0-471b-9b6d-4d2c9e8f6caa" UUID_SUB="9bfaca49-5d98-419d-b8aa-b5b93203ff29" BLOCK_SIZE="4096" TYPE="btrfs"
/dev/sda2: UUID="3a29447e-599f-42fb-b3d2-6b44e871e8d6" TYPE="crypto_LUKS" PARTLABEL="luks" PARTUUID="35d5b22d-336f-4ea0-9f2b-ecc19c6fd9f1"
/dev/sda1: LABEL_FATBOOT="EFI" LABEL="EFI" UUID="AB14-F139" BLOCK_SIZE="512" TYPE="vfat" PARTLABEL="esp" PARTUUID="3ed9525c-0a0a-48c2-b713-4116badc36e4"

Set cmdline file

File path: /mnt/etc/kernel/cmdline. loglevel=3 controls kernel log verbosity. You can set quiet if you prefer no log messages on boot (logs still saved, just not shown on boot screen).

loglevel=3

Set cmdline/root.conf

This file helps the bootloader find the root partition. Without it, system cannot locate the root and fails to boot.

File path: /mnt/etc/cmdline.d/root.conf. Create the directory first:

mkdir /mnt/etc/cmdline.d

Content:

rd.luks.name=3a29447e-599f-42fb-b3d2-6b44e871e8d6=linuxroot root=/dev/mapper/linuxroot rootfstype=btrfs rootflags=subvol=/@ rw
  • rd.luks.name=<uuid>=<linuxroot> : UUID of the luks encrypted partition (here /dev/sda2), mapped to label linuxroot.
  • root=/dev/mapper/linuxroot: refers to mapped device.
  • rootfstype=btrfs: filesystem type.
  • rootflags=subvol=/@: mount subvolume @ as root.
  • rw: mount as read-write.

Adjust mkinitcpio.conf

Mainly to modify HOOKS hook order matters; wrong order can cause boot failure. Ensure your HOOKS are the same as below when using this setup.

Hook Name Purpose
base Base system
systemd systemd init system; only with systemd-boot
autodetect Hardware autodetection
microcode CPU microcode updates
modconf Kernel module configuration
kms Kernel mode setting for graphics
keyboard Keyboard support
sd-vconsole systemd virtual console
sd-encrypt systemd disk encryption support
block Block device support
filesystems Filesystem mounting
fsck Filesystem check

File path: /mnt/etc/mkinitcpio.conf

MODULES=()

BINARIES=()

FILES=()

HOOKS=(base systemd autodetect microcode modconf kms keyboard sd-vconsole sd-encrypt block filesystems fsck)

Diff:

MODULES=()

BINARIES=()

FILES=()

-HOOKS=(base udev autodetect microcode modconf kms keyboard keymap consolefont block filesystems fsck)
+HOOKS=(base systemd autodetect microcode modconf kms keyboard sd-vconsole sd-encrypt block filesystems fsck)

Configure Unified Kernel Image (UKI)

Edit the /mnt/etc/mkinitcpio.d/linux.preset file

# mkinitcpio preset file for the 'linux' package

-#ALL_config="/etc/mkinitcpio.conf"
+ALL_config="/etc/mkinitcpio.conf"
ALL_kver="/boot/vmlinuz-linux"

PRESETS=('default' 'fallback')

#default_config="/etc/mkinitcpio.conf"
-default_image="/boot/initramfs-linux.img"
+#default_image="/boot/initramfs-linux.img"
-#default_uki="/efi/EFI/Linux/arch-linux.efi"
-#default_options="--splash /usr/share/systemd/bootctl/splash-arch.bmp"
+default_uki="/efi/EFI/Linux/arch-linux.efi"
+default_options="--splash /usr/share/systemd/bootctl/splash-arch.bmp"

-fallback_config="/etc/mkinitcpio.conf"
-fallback_image="/boot/initramfs-linux-fallback.img"
+#fallback_config="/etc/mkinitcpio.conf"
+#fallback_image="/boot/initramfs-linux-fallback.img"
-#fallback_uki="/efi/EFI/Linux/arch-linux-fallback.efi"
-#fallback_options="-S autodetect"
+fallback_uki="/efi/EFI/Linux/arch-linux-fallback.efi"
+fallback_options="-S autodetect"

Make sure the final content looks like this:

# mkinitcpio preset file for the 'linux' package

ALL_config="/etc/mkinitcpio.conf"
ALL_kver="/boot/vmlinuz-linux"

PRESETS=('default' 'fallback')

#default_config="/etc/mkinitcpio.conf"
#default_image="/boot/initramfs-linux.img"
default_uki="/efi/EFI/Linux/arch-linux.efi"
default_options="--splash /usr/share/systemd/bootctl/splash-arch.bmp"

#fallback_config="/etc/mkinitcpio.conf"
#fallback_image="/boot/initramfs-linux-fallback.img"
fallback_uki="/efi/EFI/Linux/arch-linux-fallback.efi"
fallback_options="-S autodetect"

Generate UKI

Create the directory to store the UKI:

mkdir -p /mnt/efi/EFI/Linux

Generate the UKI:

arch-chroot /mnt mkinitcpio -P

Make sure that the cmdline.d/root.conf and cmdline configurations are included during the installation process.Learned this the hard way.

root@archiso ~ # arch-chroot /mnt mkinitcpio -P
==> Building image from preset: /etc/mkinitcpio.d/linux.preset: 'default'
==> Using configuration file: '/etc/mkinitcpio.conf'
  -> -k /boot/vmlinuz-linux -c /etc/mkinitcpio.conf -U /efi/EFI/Linux/arch-linux.efi --splash /usr/share/systemd/bootctl/splash-arch.bmp
==> Starting build: '6.12.1-arch1-1'
  -> Running build hook: [base]
  -> Running build hook: [systemd]
  -> Running build hook: [autodetect]
  -> Running build hook: [microcode]
  -> Running build hook: [modconf]
  -> Running build hook: [kms]
  -> Running build hook: [keyboard]
  -> Running build hook: [sd-vconsole]
  -> Running build hook: [sd-encrypt]
==> WARNING: Possibly missing firmware for module: 'qat_420xx'
  -> Running build hook: [block]
  -> Running build hook: [filesystems]
  -> Running build hook: [fsck]
==> Generating module dependencies
==> Creating zstd-compressed initcpio image
  -> Early uncompressed CPIO image generation successful
==> Initcpio image generation successful
==> Creating unified kernel image: '/efi/EFI/Linux/arch-linux.efi'
  -> Using cmdline file: '/etc/kernel/cmdline'
  -> Using cmdline file: '/etc/cmdline.d/root.conf'
==> Unified kernel image generation successful
==> Running post hooks
  -> Running post hook: [sbctl]
Secureboot key directory doesn't exist, not signing!
==> Post processing done
==> Building image from preset: /etc/mkinitcpio.d/linux.preset: 'fallback'
==> Using configuration file: '/etc/mkinitcpio.conf'
  -> -k /boot/vmlinuz-linux -c /etc/mkinitcpio.conf -U /efi/EFI/Linux/arch-linux-fallback.efi -S autodetect
==> Starting build: '6.12.1-arch1-1'
  -> Running build hook: [base]
  -> Running build hook: [systemd]
  -> Running build hook: [microcode]
  -> Running build hook: [modconf]
  -> Running build hook: [kms]
==> WARNING: Possibly missing firmware for module: 'ast'
  -> Running build hook: [keyboard]
==> WARNING: Possibly missing firmware for module: 'xhci_pci_renesas'
  -> Running build hook: [sd-vconsole]
  -> Running build hook: [sd-encrypt]
==> WARNING: Possibly missing firmware for module: 'qat_420xx'
  -> Running build hook: [block]
==> WARNING: Possibly missing firmware for module: 'aic94xx'
==> WARNING: Possibly missing firmware for module: 'bfa'
==> WARNING: Possibly missing firmware for module: 'qed'
==> WARNING: Possibly missing firmware for module: 'qla1280'
==> WARNING: Possibly missing firmware for module: 'qla2xxx'
==> WARNING: Possibly missing firmware for module: 'wd719x'
  -> Running build hook: [filesystems]
  -> Running build hook: [fsck]
==> Generating module dependencies
==> Creating zstd-compressed initcpio image
  -> Early uncompressed CPIO image generation successful
==> Initcpio image generation successful
==> Creating unified kernel image: '/efi/EFI/Linux/arch-linux-fallback.efi'
  -> Using cmdline file: '/etc/kernel/cmdline'
  -> Using cmdline file: '/etc/cmdline.d/root.conf'
==> Unified kernel image generation successful
==> Running post hooks
  -> Running post hook: [sbctl]
Secureboot key directory doesn't exist, not signing!
==> Post processing done
arch-chroot /mnt mkinitcpio -P  25.02s user 26.74s system 110% cpu 46.944 total

Check that the files arch-linux.efi and arch-linux-fallback.efi exist:

root@archiso ~ # ls -lR /mnt/efi
/mnt/efi:
total 4
drwxr-xr-x 3 root root 4096 Dec  7 04:51 EFI

/mnt/efi/EFI:
total 4
drwxr-xr-x 2 root root 4096 Dec  7 04:52 Linux

/mnt/efi/EFI/Linux:
total 170064
-rwxr-xr-x 1 root root  30520832 Nov 29 21:43 arch-linux.efi
-rwxr-xr-x 1 root root 143618560 Nov 29 21:43 arch-linux-fallback.efi

Enable Necessary Services

Essential services

Service Description
systemd-resolved DNS resolver
systemd-timesyncd Time synchronization
NetworkManager Network management

If these services are not enabled, you may lose network connectivitythough you can enable them later after reboot.

systemctl --root /mnt enable systemd-resolved systemd-timesyncd NetworkManager
root@archiso ~ # systemctl --root /mnt enable systemd-resolved systemd-timesyncd NetworkManager
Created symlink '/mnt/etc/systemd/system/dbus-org.freedesktop.resolve1.service'
 '/usr/lib/systemd/system/systemd-resolved.service'.
Created symlink '/mnt/etc/systemd/system/sysinit.target.wants/systemd-resolved.service'
 '/usr/lib/systemd/system/systemd-resolved.service'.
Created symlink '/mnt/etc/systemd/system/dbus-org.freedesktop.timesync1.service'
 '/usr/lib/systemd/system/systemd-timesyncd.service'.
Created symlink '/mnt/etc/systemd/system/sysinit.target.wants/systemd-timesyncd.service'
 '/usr/lib/systemd/system/systemd-timesyncd.service'.
Created symlink '/mnt/etc/systemd/system/multi-user.target.wants/NetworkManager.service'
 '/usr/lib/systemd/system/NetworkManager.service'.
Created symlink '/mnt/etc/systemd/system/dbus-org.freedesktop.nm-dispatcher.service'
 '/usr/lib/systemd/system/NetworkManager-dispatcher.service'.
Created symlink '/mnt/etc/systemd/system/network-online.target.wants/NetworkManager-wait-online.service'
 '/usr/lib/systemd/system/NetworkManager-wait-online.service'.

Mask systemd-networkd

We do not use this service and it conflicts with NetworkManager, so mask it.

systemctl --root /mnt mask systemd-networkd
root@archiso ~ # systemctl --root /mnt mask systemd-networkd
Created symlink '/mnt/etc/systemd/system/systemd-networkd.service' '/dev/null'.

Install systemd-boot Bootloader

Write the UKI to the bootloader so the machine knows how to boot:

arch-chroot /mnt bootctl install --esp-path=/efi
root@archiso ~ # arch-chroot /mnt bootctl install --esp-path=/efi

Check and you should see new files like BOOTX64.EFI, systemd-bootx64.efi, entries.srel, loader.conf, and random-seed indicating a successful install:

root@archiso ~ # ls -lR /mnt/efi
/mnt/efi:
total 8
drwxr-xr-x 5 root root 4096 Dec  7 05:00 EFI
drwxr-xr-x 3 root root 4096 Dec  7 05:00 loader

/mnt/efi/EFI:
total 12
drwxr-xr-x 2 root root 4096 Dec  7 05:00 BOOT
drwxr-xr-x 2 root root 4096 Dec  7 04:52 Linux
drwxr-xr-x 2 root root 4096 Dec  7 05:00 systemd

/mnt/efi/EFI/BOOT:
total 104
-rwxr-xr-x 1 root root 105472 Nov 29 21:43 BOOTX64.EFI

/mnt/efi/EFI/Linux:
total 170064
-rwxr-xr-x 1 root root  30520832 Nov 29 21:43 arch-linux.efi
-rwxr-xr-x 1 root root 143618560 Nov 29 21:43 arch-linux-fallback.efi

/mnt/efi/EFI/systemd:
total 104
-rwxr-xr-x 1 root root 105472 Nov 29 21:43 systemd-bootx64.efi

/mnt/efi/loader:
total 16
drwxr-xr-x 2 root root 4096 Dec  7 05:00 entries
-rwxr-xr-x 1 root root    6 Dec  7 05:00 entries.srel
-rwxr-xr-x 1 root root   30 Dec  7 05:00 loader.conf
-rwxr-xr-x 1 root root   32 Dec  7 05:00 random-seed

/mnt/efi/loader/

Reboot

sync
systemctl reboot

After reboot, you will see a prompt to enter the disk encryption password. Since no password is set now, just press Enter. Then the system will boot, though no desktop environment is installed yet. That will be covered later.

Secure Boot

After login, configure Secure Boot[^2].

Check status

sudo sbctl status

You will see Secure Boot is currently disabled, and Setup Mode is enabled.

admin@arch-tmp ~> sudo sbctl status
Installed: sbctl is not installed
Setup Mode: Enabled
Secure Boot: Disabled
Vendor Keys: none

Create keys

sudo sbctl create-keys

This will create platform keys and show the platform key UUID.

admin@arch-tmp ~> sudo sbctl create-keys
Created Owner UUID c5053c9a-3643-436a-ae0c-1bacc689cc6a
Creating secure boot keys...✓
Secure boot keys created!

Enroll Microsoft keys

This is important because many hardware devices use Microsoft’s platform keys for signing. Without enrolling them, your system might fail to boot. If you are on a virtual machine, errors may occur but can be ignored.

sudo sbctl enroll-keys -m

Check status again

sudo sbctl status

You should now see Vendor Keys includes microsoft, which is important.

admin@arch-tmp ~> sudo sbctl status
Installed: sbctl is installed
Owner GUID: c5053c9a-3643-436a-ae0c-1bacc689cc6a
Setup Mode: Enabled
Secure Boot: Disabled
Vendor Keys: microsoft

Verify signatures

sudo sbctl verify

You will see that the UKI and bootloader files are unsigned.

admin@arch-tmp ~> sudo sbctl verify
Verifying file database and EFI images in /efi...
 /efi/EFI/BOOT/BOOTX64.EFI is not signed
 /efi/EFI/Linux/arch-linux-fallback.efi is not signed
 /efi/EFI/Linux/arch-linux.efi is not signed
 /efi/EFI/systemd/systemd-bootx64.efi is not signed

Sign the files

sudo sbctl sign -s /efi/EFI/BOOT/BOOTX64.EFI
sudo sbctl sign -s /efi/EFI/Linux/arch-linux.efi
sudo sbctl sign -s /efi/EFI/Linux/arch-linux-fallback.efi
sudo sbctl sign -s /efi/EFI/systemd/systemd-bootx64.efi
admin@arch-tmp ~> sudo sbctl sign -s /efi/EFI/BOOT/BOOTX64.EFI
 Signed /efi/EFI/BOOT/BOOTX64.EFI
admin@arch-tmp ~> sudo sbctl sign -s /efi/EFI/Linux/arch-linux.efi
 Signed /efi/EFI/Linux/arch-linux.efi
admin@arch-tmp ~> sudo sbctl sign -s /efi/EFI/Linux/arch-linux-fallback.efi
 Signed /efi/EFI/Linux/arch-linux-fallback.efi
admin@arch-tmp ~> sudo sbctl sign -s /efi/EFI/systemd/systemd-bootx64.efi
 Signed /efi/EFI/systemd/systemd-bootx64.efi

Configure TPM Auto-Unlock

Please reboot before proceeding to correctly configure TPM auto-unlock.

Enroll recovery key

Register a recovery key (very important). Enter the current passwordcurrently there is no password to generate a recovery key, then save it securely.

sudo systemd-cryptenroll --recovery-key /dev/sda2
admin@arch-tmp ~> sudo systemd-cryptenroll --recovery-key /dev/sda2
🔐 Please enter current passphrase for disk /dev/sda2:
A secret recovery key has been generated for this volume:

    🔐 bgjdrdgt-cilfdtur-lhgcruln-idbglifj-bfuffejf-kherljfl-trdnrlju-gnfutnun

Please save this secret recovery key at a secure location. It may be used to
regain access to the volume if the other configured access credentials have
been lost or forgotten. The recovery key may be entered in place of a password
whenever authentication is requested.
New recovery key enrolled as key slot 1.

Enroll TPM2 Auto-Unlock

Note that the --tpm2-device option can automatically select the TPM device if you specify auto. If you have multiple TPM devices, you may need to configure it manually. After entering your password, the TPM key will be enrolled to a slot.

Also note that you can specify PCRs with --tpm2-pcrs=0+7+11. If not specified, it defaults to PCR 7.

sudo systemd-cryptenroll --tpm2-device=auto /dev/sda2
admin@arch-tmp ~> sudo systemd-cryptenroll --tpm2-device=auto /dev/sda2
🔐 Please enter current passphrase for disk /dev/sda2:
New TPM2 token enrolled as key slot 2.

Check TPM2 Slots

sudo systemd-cryptenroll /dev/sda2

You will notice slot 0 is currently the default password slot, which is empty and should be removed. By default, TPM will handle key unlocking automatically. If unlocking fails, you can use the recovery key to access the system.

admin@arch-tmp ~> sudo systemd-cryptenroll /dev/sda2
SLOT TYPE
   0 password
   1 recovery
   2 tpm2

Remove Empty Password Slot

sudo systemd-cryptenroll /dev/sda2 --wipe-slot empty
admin@arch-tmp ~> sudo systemd-cryptenroll /dev/sda2 --wipe-slot empty
Wiped slot 0.

Check Again

admin@arch-tmp ~> sudo systemd-cryptenroll /dev/sda2
SLOT TYPE
   1 recovery
   2 tpm2

Footnotes

  1. Dm-crypt/Encrypting the Entire System

  2. UEFI/Secure Boot 2

  3. wiki/Unified Kernel Image

  4. Btrfs

  5. Trusted Platform Module

  6. Trusted Platform Module#Accessing_PCR_Registers

  7. Cold boot attack

  8. Cold boot attack Full memory encryption

  9. UEFI/Secure Boot#Backup_Current_Variables

  10. https://www.dwarmstrong.org/archlinux-install/

  11. GPT_fdisk#Partition_type

  12. https://walian.co.uk/

  13. https://bbs.archlinux.org/viewtopic.php?id=295495


Backlink