Arch Linux installation guide
Btrfs on LUKS, separate EFI partition on /efi
, GNOME, systemd-boot, TPM2
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
[OPTIONAL] 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
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:
g
ENTER
Create the EFI system partition, allocating 1G of space:
n
ENTER
ENTER
ENTER
+1G
ENTER
# Change the partition type
t
ENTER
Change the partition type to “EFI System”:
t
ENTER
1
ENTER
Create the Linux root partition, allocating all remaining space:
n
ENTER
ENTER
ENTER
ENTER
Change the partition type to “Linux root (x86_64)”
t
ENTER
2
ENTER
23
ENTER
Check the provisional partitions:
p
ENTER
Save the changes, or discard if you want to start over:
# To save
w
ENTER
# To discard
q
ENTER
Format the partitions
For the EFI system partition:
mkfs.fat -F 32 /dev/sda1
For the Linux root partition, first create a LUKS volume with a blank password which will be wiped later$^\text{[verification needed]}$:
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
Select the mirrors:
reflector --country <country> --sort rate --save /etc/pacman.d/mirrorlist
Install packages (for more details, refer to https://github.com/Bigstool/i-use-arch-btw/blob/main/packages.md):
pacstrap -K /mnt base base-devel linux-lts linux-lts-headers linux linux-headers linux-firmware btrfs-progs dosfstools mtools amd-ucode sudo man-db vim nano networkmanager bluez ufw openssh git reflector cronie zram-generator snapper rsync zsh zsh-completions zsh-autosuggestions pipewire pipewire-alsa pipewire-pulse pipewire-jack wireplumber noto-fonts-cjk noto-fonts fuse2 gnome gnome-tweaks gnome-themes-extra gdm gnome-browser-connector guake ibus ibus-rime ibus-anthy firefox carla cryptsetup sbctl
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
Configure time
Set the timezone:
ln -sf /usr/share/zoneinfo/Region/City /etc/localtime
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
:
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 the /etc/mkinitcpio.d/linux.preset
and /etc/mkinitcpio.d/linux-lts.preset
files, comment out the _image
fields, uncomment the _uki
and _options
fields. Then, change the paths of the .efi
files to /efi/EFI/...
.
As an example, the linux.preset
file should now look somewhat 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"
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
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.
NOTE: It is assumed that all shell commands from this point onward are run as the newly created user with sudo privilege.
Secure boot
(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)
Set secure boot mode to setup mode by booting the PC into firmware setup and clear the secure boot keys, then reboot into the new system. Verify that setup mode is indeed activated with:
sbctl status
If yes, create the custom secure boot keys:
sudo sbctl create-keys
Enroll the keys with Microsoft’s keys to the UEFI:
NOTE: Do NOT omit the -m
flag below, otherwise it might brick the device.
sudo sbctl enroll-keys -m
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.efi
sudo sbctl sign -s /efi/EFI/Linux/arch-linux-fallback.efi
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 with secure boot turned back on in the firmware settings, and check that secure boot is working with:
sbctl status
Enroll TPM
Create a recovery key:
sudo systemd-cryptenroll /dev/sda2 --recovery-key
Enroll the key:
sudo systemd-cryptenroll /dev/sda2 --wipe-slot=empty --tpm2-device=auto --tpm2-pcrs=7
Reboot to see if the drive is automatically unlocked. The installation is complete.
NOTE: The combination of PCRs to bind given here is for illustration purpose only and can lead to leakage of the encryption key. Do refer to other sources including https://wiki.archlinux.org/title/Trusted_Platform_Module#Accessing_PCR_registers and https://wiki.archlinux.org/title/Systemd-cryptenroll#Trusted_Platform_Module for more information, then choose the combination that works for the particular setup and threat model.
To change the enrolled TPM key, wipe the existing slot first with:
sudo systemd-cryptenroll /dev/sda2 --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 -S snapper
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/
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"
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
Install Btrfs Assistant with:
yay -Syu btrfs-assistant
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/Snapper#Suggested_filesystem_layout