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:
- An attacker tries to steal the disk to read data. LUKS encryption prevents this by encrypting the disk.
- An attacker tries to load a malicious kernel to read the disk. Secure Boot blocks unsigned kernels.
- An attacker tries to modify boot parameters to steal the disk unlock key. TPM detects changes (PCR 8) and denies auto-unlock.
- 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.eslEnter 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
lsblkroot@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/bootmntYou 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 $diskCreate a new GPT partition table:
sgdisk --zap-all --clear $diskReload partition table:
partprobe $diskroot@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 targetFill 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=directClose encrypted device:
cryptsetup close targetroot@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 targetPartitioning
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 $diskThis 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 $diskReload partition table:
partprobe $diskroot@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 $diskCheck partition table:
sgdisk -p $diskroot@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 luksEncryption
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}2root@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 totalOpen the encrypted block device
cryptsetup open ${disk}2 linuxrootroot@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 totalFormat file systems
Format the EFI partition:
mkfs.vfat -F32 -n EFI /dev/sda1root@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/linuxrootroot@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/linuxrootCreate Btrfs subvolumes
Mount the root partition:
mount /dev/mapper/linuxroot /mntCreate 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/@rootroot@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 /mntroot@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 @rootUnmount root partition:
umount /mntRemount 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/rootMount EFI partition:
mount -m /dev/sda1 /mnt/efiDeprecated 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/@containersCheck subvolumes list:
btrfs subvolume list /mntUnmount root:
umount /mntRemount 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/srvDisabling 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/tmpCurrently, 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/bootmntInstalling 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/mirrorlistThe 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
#DisableSandboxBase 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 sudoGenerate fstab file
This saves the mounts you painstakingly set up; otherwise they won’t persist after reboot.
genfstab -U /mnt >> /mnt/etc/fstabCheck 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 2Localization
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.genBesides 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 --promptroot@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-genroot@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-8Add 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 adminSet password for the user, don’t forget.
arch-chroot /mnt passwd adminroot@archiso ~ # arch-chroot /mnt useradd -G wheel -m admin
root@archiso ~ # arch-chroot /mnt passwd admin
New password:
Retype new password:
passwd: password updated successfullyAvoid 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/adminAdjust 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=3Set 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.dContent:
rd.luks.name=3a29447e-599f-42fb-b3d2-6b44e871e8d6=linuxroot root=/dev/mapper/linuxroot rootfstype=btrfs rootflags=subvol=/@ rwrd.luks.name=<uuid>=<linuxroot>: UUID of the luks encrypted partition (here/dev/sda2), mapped to labellinuxroot.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/LinuxGenerate the UKI:
arch-chroot /mnt mkinitcpio -PMake 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 totalCheck 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.efiEnable 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 NetworkManagerroot@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-networkdroot@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=/efiroot@archiso ~ # arch-chroot /mnt bootctl install --esp-path=/efiCheck 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 rebootAfter 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 statusYou 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: noneCreate keys
sudo sbctl create-keysThis 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 -mCheck status again
sudo sbctl statusYou 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: microsoftVerify signatures
sudo sbctl verifyYou 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 signedSign 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.efiadmin@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.efiConfigure 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/sda2admin@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/sda2admin@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/sda2You 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 tpm2Remove Empty Password Slot
sudo systemd-cryptenroll /dev/sda2 --wipe-slot emptyadmin@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