Arch Linux installation guide
Btrfs on LUKS, separate EFI partition on /efi, GNOME, systemd-boot, TPM2
Before you proceed, you’re recommended to refer to https://github.com/Bigstool/i-use-arch-btw/blob/main/installation-guides.md first. Compare and choose a path or a combination of paths suitable for your use case.
Essentials
Keyboard layout
Boot the Arch Linux installation image, then configure the console keyboard layout with:
loadkeys us
Available keys can be listed with:
localectl list-keymaps
Check the boot mode is UEFI
cat /sys/firmware/efi/fw_platform_size
You are in UEFI if the returned value is 64 or 32.
Check the internet connection
ping -c 5 archlinux.org
Check the system clock
Check that NTP service is active:
timedatectl
Partition the disk
The partition layout being used is as follows:
+-----------------------+---------------------------------+
| EFI system partition | LUKS encrypted root partition |
| | |
| | |
| /efi | / |
| | |
| | /dev/mapper/cryptroot |
| |---------------------------------|
| /dev/sda1 | /dev/sda2 |
+-----------------------+---------------------------------+
Find the right disk with:
fdisk -l
Partition the disk using /dev/sda as example:
fdisk /dev/sda
Create a new GPT partition table:
Command (m for help): g
# Expected output
Created a new GPT disklabel (GUID: C1BCB28A-66E7-4DE9-89CC-B808E30243B3).
Create the EFI system partition, allocating 1G of space:
Command (m for help): n
Partition number (1-128, default 1): <ENTER>
First sector (2048-468862094, default 2048): <ENTER>
Last sector, +/-sectors or +/-size{K,M,G,T,P} (2048-468862094, default 468860927): +1G
# Expected output
Created a new partition 1 of type 'Linux filesystem' and of size 1 GiB.
Change the partition type to “EFI System”:
Command (m for help): t
Selected partition 1
Partition type or alias (type L to list all): 1
# Expected output
Changed type of partition 'Linux filesystem' to 'EFI System'.
Create the Linux root partition, allocating all remaining space:
Command (m for help): n
Partition number (2-128, default 2): <ENTER>
First sector (2099200-468862094, default 2099200): <ENTER>
Last sector, +/-sectors or +/-size{K,M,G,T,P} (2099200-468862094, default 468860927): <ENTER>
# Expected output
Created a new partition 2 of type 'Linux filesystem' and of size 222.6 GiB.
Change the partition type to “Linux root (x86_64)”
Command (m for help): t
Partition number (1,2, default 2): <ENTER>
Partition type or alias (type L to list all): 23
# Expected output
Changed type of partition 'Linux filesystem' to 'Linux root (x86-64)'.
Check the provisional partitions:
Command (m for help): p
Save the changes, or discard if you want to start over:
# To save
Command (m for help): w
# To discard
Command (m for help): q
Format the partitions
For the EFI system partition:
mkfs.fat -F 32 /dev/sda1
For the Linux root partition, create a LUKS volume with a strong password:
cryptsetup luksFormat /dev/sda2
cryptsetup open /dev/sda2 cryptroot
The volume will be available at /dev/mapper/cryptroot. Create the Btrfs file system with:
mkfs.btrfs /dev/mapper/cryptroot
Create subvolumes for the Btrfs file system
Mount the file system to /mnt:
mount /dev/mapper/cryptroot /mnt
Create the subvolumes as desired (refer to https://wiki.archlinux.org/title/Snapper#Suggested_filesystem_layout for other layouts):
btrfs subvolume create /mnt/@
btrfs subvolume create /mnt/@home
btrfs subvolume create /mnt/@log
btrfs subvolume create /mnt/@cache
btrfs subvolume create /mnt/@tmp
btrfs subvolume create /mnt/@snapshots
Unmount:
umount /mnt
Mount the file systems and subvolumes
Mount the root:
mount -o compress=zstd,subvol=@ /dev/mapper/cryptroot /mnt
Create the directories to mount to:
mkdir -p /mnt/{home,var/log,var/cache,var/tmp,.snapshots,efi}
Mount the rest:
mount -o compress=zstd,subvol=@home /dev/mapper/cryptroot /mnt/home
mount -o compress=zstd,subvol=@log /dev/mapper/cryptroot /mnt/var/log
mount -o compress=zstd,subvol=@cache /dev/mapper/cryptroot /mnt/var/cache
mount -o compress=zstd,subvol=@tmp /dev/mapper/cryptroot /mnt/var/tmp
mount -o compress=zstd,subvol=@snapshots /dev/mapper/cryptroot /mnt/.snapshots
mount /dev/sda1 /mnt/efi
Install the system
Refer to Select the mirrors to configure pacman mirrors.
Install base packages (for more details, refer to Packages):
pacstrap -K /mnt base base-devel linux-lts linux-lts-headers linux-firmware btrfs-progs dosfstools mtools cryptsetup sudo man-db vim nano cronie networkmanager reflector openssh git
Fstab
Generate Fstab config w.r.t. how the file systems and subvolumes are mounted now:
genfstab -U /mnt >> /mnt/etc/fstab
Chroot into the new system
arch-chroot /mnt
Install packages
TipTake a look at the packages below and make adjustments according to preference and hardware. For example, Intel users should install
intel-ucodeinstead ofamd-ucode. For more details, refer to https://github.com/Bigstool/i-use-arch-btw/blob/main/packages.md.
pacman -Syu amd-ucode bluez fuse2 zsh pipewire pipewire-alsa pipewire-pulse pipewire-jack wireplumber rsync noto-fonts-cjk noto-fonts gnome gnome-tweaks gnome-themes-extra gdm gnome-browser-connector guake firefox ibus ibus-rime ibus-anthy
Configure time
Set the timezone:
ln -sf /usr/share/zoneinfo/<Area>/<Location> /etc/localtime
TipOptionally, set the system to read the RTC time in the local time zone. This is useful when Windows will be run on the same computer:
timedatectl set-local-rtc 1
Sync to the hardware clock:
hwclock --systohc
Locale and keymap
Uncomment the locales that you wish to generate:
nano /etc/locale.gen
Generate the selected locales by:
locale-gen
Set the system language in /etc/locale.conf:
LANG=en_US.UTF-8
Set the keymap in /etc/vconsole.conf:
KEYMAP=us
Hostname
Put the hostname in the first line of /etc/hostname. For example, if the desired hostname is arch:
arch
Edit /etc/hosts to include the hostname:
127.0.0.1 localhost
::1 localhost
127.0.1.1 arch
Create user
Create a new user in the administration group:
useradd -mG wheel bigstool
Create a password for the new user:
passwd bigstool
Grant sudo privilege to the new user:
EDITOR=nano visudo
Add the following line under # User privilege specification:
bigstool ALL=(ALL) NOPASSWD: ALL
Verify that the privilege has been granted:
# As root
su - bigstool
# As the new user
sudo echo hello # Should print "hello" to the console
exit
Configure the default Btrfs subvolume
Specifically for the Btrfs file system, find the subvolume ID of the root subvolume of the Btrfs file system with:
btrfs subvolume list -t /
The output should include something like this:
ID gen top level path
-- --- --------- ----
256 370 5 @
The ID of path @ is what to look for. In this case, it is 256.
Change the default subvolume, replace <id> with the real value:
btrfs subvolume set-default <id> /
Configure mkinitcpio and unified kernel image
Build a working systemd based initramfs by modifying the HOOKS of /etc/mkinitcpio.conf:
HOOKS=(base *systemd* autodetect microcode modconf kms *keyboard* *sd-vconsole* block *sd-encrypt* filesystems fsck)
Pay attention to the items surrounded with *asterisks* and add them without the asterisks.
Find the UUID of the encrypted /dev/sda2 partition with:
blkid
The output should include something like this:
/dev/sda2: UUID="06b34979-42f7-4033-95bd-6587b49191a0" TYPE="crypto_LUKS" PARTUUID="642251ea-aa7c-4a7c-ba49-3e41ccebeb3e"
The UUID (not the PARTUUID) is what to look for. Edit the kernel command line with:
nano /etc/kernel/cmdline
Add the following, then save and exit:
rd.luks.name=<UUID>=cryptroot root=/dev/mapper/cryptroot
In the example, it is:
rd.luks.name=06b34979-42f7-4033-95bd-6587b49191a0=cryptroot root=/dev/mapper/cryptroot
Next, edit /etc/mkinitcpio.d/linux-lts.preset (or /etc/mkinitcpio.d/linux.preset if using the linux kernel), comment out the _image fields, uncomment the _uki and _options fields. Then, change the paths of the .efi files to /efi/EFI/....
The linux-lts.preset file should now look somewhat like this:
# mkinitcpio preset file for the 'linux-lts' package
#ALL_config="/etc/mkinitcpio.conf"
ALL_kver="/boot/vmlinuz-linux-lts"
#ALL_kerneldest="/boot/vmlinuz-linux-lts"
#PRESETS=('default')
PRESETS=('default' 'fallback')
#default_config="/etc/mkinitcpio.conf"
#default_image="/boot/initramfs-linux-lts.img"
default_uki="/efi/EFI/Linux/arch-linux-lts.efi"
default_options="--splash /usr/share/systemd/bootctl/splash-arch.bmp"
#fallback_config="/etc/mkinitcpio.conf"
#fallback_image="/boot/initramfs-linux-lts-fallback.img"
fallback_uki="/efi/EFI/Linux/arch-linux-lts-fallback.efi"
fallback_options="-S autodetect"
If the initramfs was generated previously, remove the residual initramfs files in /boot as they are no longer relevant with UKI:
rm /boot/initramfs-linux-*
Additional note: https://wiki.archlinux.org/title/Unified_kernel_image#pacman_hook
Boot loader
Install the systemd-boot boot loader with:
bootctl install
Optionally, configure the boot loader to display a menu for kernel selection at boot by editing /efi/loader/loader.conf (ref: https://wiki.archlinux.org/title/Systemd-boot#Loader_configuration, https://www.freedesktop.org/software/systemd/man/latest/loader.conf.html):
default @saved
timeout 3
console-mode max
editor no
Finalizing the installation
Regenerate initramfs with:
mkinitcpio -P
Enable services with:
systemctl enable NetworkManager sshd cronie gdm bluetooth
Exit from chroot:
exit
Sync and unmount everything:
sync
umount -R /mnt
Shut down:
poweroff
First boot
Try booting the new system. If it works, shut down the PC.
NoteIt is assumed that all shell commands from this point onward are run as the newly created user with sudo privilege.
Secure boot
Install sbctl:
sudo pacman -Syu sbctl
Reboot the PC into firmware setup. Under the secure boot settings, put secure boot into enter setup mode by deleting the Platform Key (PK). If this is not possible, delete/clear the secure boot keys. If the firmware offers an “OS type” option, choose Windows instead of other OS, as this setup will be Microsoft secure boot compatible. (ref: https://wiki.archlinux.org/title/Unified_Extensible_Firmware_Interface/Secure_Boot#Putting_firmware_in_%22Setup_Mode%22, https://man.archlinux.org/man/sbctl.8#USAGE)
Boot back into Arch and verify that setup mode is indeed activated with:
sbctl status
If yes, create the custom secure boot keys:
sudo sbctl create-keys
The keys are stored in /var/lib/sbctl/keys.
Enroll the keys alongside Microsoft’s keys and the OEM firmware’s built-in certificates to the UEFI:
WarningDo NOT omit the
-mflag below, otherwise it might brick the device.
sudo sbctl enroll-keys -m -f
TipIf the command above runs into the following error:
‼ File is immutable: /sys/firmware/efi/efivars/KEK-732f2b7e-7013-4e43-be44-d0de168a3d92 ‼ File is immutable: /sys/firmware/efi/efivars/db-141c4108-7a45-4de4-929a-4b27d558b59c You need to chattr -i files in efivarfsTemporarily remove the immutable flag of the UEFI secure boot variables with:
sudo chattr -i /sys/firmware/efi/efivars/KEK-732f2b7e-7013-4e43-be44-d0de168a3d92 sudo chattr -i /sys/firmware/efi/efivars/db-141c4108-7a45-4de4-929a-4b27d558b59cThen, try enrolling the keys again.
Check the status again, sbctl should be installed now:
sbctl status
Check what files need to be signed:
sudo sbctl verify
Sign all the unsigned files. For example:
sudo sbctl sign -s /efi/EFI/Linux/arch-linux-lts.efi
sudo sbctl sign -s /efi/EFI/Linux/arch-linux-lts-fallback.efi
sudo sbctl sign -s /efi/EFI/BOOT/BOOTX64.EFI
sudo sbctl sign -s /efi/EFI/systemd/systemd-bootx64.efi
TipIf an undesired file was accidentally signed, remove the file from the signing database with:
sudo sbctl remove-file <file>
For systemd-boot, sign the boot loader directly in /usr/lib as well (ref: https://wiki.archlinux.org/title/Unified_Extensible_Firmware_Interface/Secure_Boot#Automatic_signing_with_the_pacman_hook):
sudo sbctl sign -s -o /usr/lib/systemd/boot/efi/systemd-bootx64.efi.signed /usr/lib/systemd/boot/efi/systemd-bootx64.efi
Reboot, and check that secure boot is working with:
sbctl status
Enroll TPM
Enroll the key, replace <UUID> with the one used in Configure mkinitcpio and unified kernel image:
sudo systemd-cryptenroll /dev/disk/by-uuid/<UUID> --wipe-slot=empty --tpm2-device=auto --tpm2-pcrs=0+2+5+7+15:sha256=0000000000000000000000000000000000000000000000000000000000000000
0: Core System Firmware executable code (aka Firmware). May change if you upgrade your UEFI.
2: Extended or pluggable executable code; includes option ROMs on pluggable hardware.
5: GPT/Partition table; changes when the partitions are added, modified, or removed.
7: Secure Boot state; changes when UEFI SecureBoot mode is enabled/disabled, or firmware certificates (PK, KEK, db, dbx, …) changes.
15: Root LUKS volume key, machine ID, mount points, file system UUIDs, labels, partition UUIDs; starts being all zero at boot.
WarningOnly binding to PCRs 0-7 can introduce vulnerabilities. Refer to sources including https://wiki.archlinux.org/title/Systemd-cryptenroll#Trusted_Platform_Module, https://wiki.archlinux.org/title/Trusted_Platform_Module#Accessing_PCR_registers, https://uapi-group.org/specifications/specs/linux_tpm_pcr_registry/, and https://man.archlinux.org/man/systemd-cryptenroll.1 for more information, then choose the combination that works for the particular setup and threat model.
Append the following to /etc/kernel/cmdline:
rd.luks.options=cryptroot:tpm2-measure-pcr=yes
Regenerate initramfs:
sudo mkinitcpio -P
Reboot to see if the drive is automatically unlocked. The installation is complete.
To change the enrolled TPM key, wipe the existing slot first with:
sudo systemd-cryptenroll /dev/disk/by-uuid/<UUID> --wipe-slot=tpm2
Then, enroll the key again with modified PCR.
Optionals
Post install considerations
Refer to https://barn.bigstool.com/post/server-config/
Further configurations for pacman
Refer to https://github.com/Bigstool/i-use-arch-btw/blob/main/pacman.md
Install and configure additional packages
Take a further look at https://github.com/Bigstool/i-use-arch-btw/blob/main/packages.md
Configure Snapper snapshots
Install Snapper
sudo pacman -Syu snapper
Configure mount point
Ref: https://wiki.archlinux.org/title/Snapper#Suggested_filesystem_layout
Unmount and remove the /.snapshots directory since Snapper assumes that /.snapshots is not mounted and does not exist as a folder:
sudo umount /.snapshots
sudo rm -r /.snapshots
Create a new configuration for /:
sudo snapper -c root create-config /
Delete the subvolume .snapshots newly created by Snapper:
sudo btrfs subvolume delete /.snapshots
Recreate the /.snapshots directory:
sudo mkdir /.snapshots
Mount @snapshots to /.snapshots utilizing the existing fstab entry:
sudo mount -a
Verify the mount with:
findmnt -nt btrfs
Give the folder 750 permissions:
sudo chmod 750 /.snapshots/
Configure automatic snapshots
Automatic timeline snapshots is enabled by default with a cron daemon correctly set up. Edit the configurations in /etc/snapper/configs/root to liking. For example:
TIMELINE_MIN_AGE="1800"
TIMELINE_LIMIT_HOURLY="5"
TIMELINE_LIMIT_DAILY="7"
TIMELINE_LIMIT_WEEKLY="0"
TIMELINE_LIMIT_MONTHLY="0"
TIMELINE_LIMIT_YEARLY="0"
GUI helper
Btrfs Assistant is a GUI tool that can manage Btrfs subvolumes and Snapper snapshots. Install Btrfs Assistant with:
sudo pacman -Syu btrfs-assistant
Backup /efi
The unified kernel images are stored in the EFI system partition and will not be included in the Btrfs snapshots. In case of kernel updates, returning to a snapshot with older kernel version would draw the system unbootable (ref: https://wiki.archlinux.org/title/EFI_system_partition#Typical_mount_points). Therefore, we create a post hook in mkinitcpio to make a backup of /efi every time the unified kernel image is generated to be later restored with the snapshot.
Create the target directory of backup:
sudo mkdir /efibak
Create the following script as /etc/initcpio/post/efibackup.sh:
#!/bin/bash
echo "Backing up /efi to /efibak..."
rsync -a --delete /efi/ /efibak/
echo "Done!"
Give the script permissions to execute:
sudo chmod +x /etc/initcpio/post/efibackup.sh
Regenerate initramfs with:
sudo mkinitcpio -P
TODO: run the post hook after sbctl
Preparations for restoring snapshots
Create a temporary mount point for the restored root for later restoration of /efi:
sudo mkdir /newroot
Restore a Snapper snapshot
Restore the snapshot
In Btrfs Assisatnt, restore the desired snapshot at Snapper > Browse/Restore.
Change the default Btrfs root to the new @ subvolume
Refer to Configure the default Btrfs subvolume. Note that the commands need to be run with sudo privilege.
Restore /efi
Mount the new root to /newroot with:
sudo mount -o compress=zstd,subvol=@ /dev/mapper/cryptroot /newroot
Overwrite /efi with the backup included in the snapshot:
sudo rsync -a --delete /newroot/efibak/ /efi/
Unmount:
sudo umount /newroot
Reboot and verify
Reboot:
sudo reboot
The system should boot without any problem. After logging in, verify that / is indeed mounted with @ with:
findmnt -nt btrfs
The output should look something like this:
/ /dev/mapper/cryptroot[/@] btrfs rw,relatime,compress=zstd:3,ssd,space_cache=v2,subvolid=300,subvol=/@
Make sure that /dev/mapper/cryptroot[/@] is mounted at /.
Additional Notes
On mounting the top-level Btrfs filesystem after installation
sudo mount -o subvolid=5 /dev/mapper/cryptroot /mnt
This prevents mounting @ instead.
References
https://wiki.archlinux.org/title/Installation_guide#Mount_the_file_systems
https://wiki.archlinux.org/title/Dm-crypt/Encrypting_an_entire_system#LUKS_on_a_partition
https://gist.github.com/mjkstra/96ce7a5689d753e7a6bdd92cdc169bae
https://itsfoss.com/wrong-time-dual-boot/
https://wiki.archlinux.org/title/Unified_kernel_image#mkinitcpio
https://wiki.archlinux.org/title/Btrfs#Mounting_subvolume_as_root
ChatGPT
https://wiki.archlinux.org/title/Mkinitcpio#Post_hooks
Doubao