Joining in the “by the way” club, finally.

Background

The reason someone always do distro hopping is they actually don’t know what options are there to choose, then always be distracted from shining fancy components. This is the most thing I learned after reading through the ArchWiki.

It took me roughly a month searching and thinking about the wiki, when finished, I realized 2 things. One, I have designed my own workstation build during this process. Two, I could implement this build on top of nearly any distros now, distro is not a problem for me anymone.

If you still do distro hopping and want to settle down, go reading through the ArchWiki, you will get answers, this is my advice.

Official Guide

Installation guide - ArchWiki

Preparation

Download Arch ISO, create bootable USB using ventoy or rufus, disable Secure Boot, boot ISO.

Enlarge console font by running command "setfont ter-132b" if needed.

The reflector didn’t work well for me, I had to pick mirror servers manually then wrote to the mirrorlist:

# cat /etc/pacman.d/mirrorlist <<EOB
Server = https://mirrors.aliyun.com/archlinux/\$repo/os/\$arch
Server = https://mirrors.tuna.tsinghua.edu.cn/archlinux/\$repo/os/\$arch
Server = https://mirrors.ustc.edu.cn/archlinux/\$repo/os/\$arch
EOB

Partition Disk

When using GPT, it is advised to follow the Discoverable Partitions Specification since systemd-gpt-auto-generator can automount them. The EFI system partition, XBOOTLDR partition, swap partition and home partition types can be changed using the set command, while for the root partition and others, you will need to specify the partition type UUID manually with the type command.
Ref: Parted#Partition schemes

EFI system partition on a GUID Partition Table is identified by the partition type GUID c12a7328-f81f-11d2-ba4b-00a0c93ec93b. Parted can set it automatically, create a partition with fat32 as the file system type and set the esp flag on it.
Ref: EFI system partition#GPT partitioned disks, Parted#UEFI/GPT examples

Root partition type GUID should be “root partition” not “LUKS partition”, which is 4f68bce3-e8cd-4db1-96e7-fbcaf984b709.
Ref: dm-crypt/Encrypt an entire system/Configuring the boot loader

Partition alignment need to be handled manually with parted.
Ref: Parted#Alignment

Disk file name would be like /dev/sda, /dev/nvme0n1, /dev/mmcblk0, /dev/vda.
Ref: Device file#Block devices

# parted /dev/vda
(parted) mklabel gpt
(parted) mkpart efipart fat32 1MiB 1025MiB
(parted) set 1 esp on
(parted) mkpart cryptpart ext4 1025MiB 100%
(parted) type 2 4f68bce3-e8cd-4db1-96e7-fbcaf984b709
(parted) quit

All partitions that have partition labels are listed in the /dev/disk/by-partlabel directory.
Ref: Persistent block device naming#by-partlabel

# ls -l /dev/disk/by-partlabel
total 0
lrwxrwxrwx 1 root root ... efipart -> ../../vda1
lrwxrwxrwx 1 root root ... rootpart -> ../../vda2

EFI Partition

Ref: EFI system partition#Format the partition

# mkfs.fat -F32 /dev/disk/by-partlabel/efipart

LUKS

Ref: dm-crypt/Device encryption#Formatting LUKS partitions ,
dm-crypt/Device encryption#Unlocking/Mapping LUKS partitions with the device mapper

Format LUKS partition and open:

# cryptsetup luksFormat /dev/disk/by-partlabel/rootpart
# cryptsetup open /dev/disk/by-partlabel/rootpart root
# ls /dev/mapper/root

Btrfs

Ref: Btrfs#File system on a single device , Btrfs#Subvolumes , Snapper#Suggested filesystem layout

Create Btrfs filesystem:

# mkfs.btrfs /dev/mapper/root
# mount /dev/mapper/root /mnt

# btrfs subvolume create /mnt/@
# btrfs subvolume create /mnt/@home
# btrfs subvolume create /mnt/@var
# btrfs subvolume create /mnt/@data
# umount /mnt

There’s a bit extra work needed to let @var subvolume work without issue, demonstrated at section Move PacmanDB of this post.

Mount Filesystem

Ref: Btrfs#Compression , EFI system partition#Typical mount points

# mount -o subvol=@ /dev/mapper/root /mnt
# mount -o subvol=@home --mkdir /dev/mapper/root /mnt/home
# mount -o subvol=@var --mkdir /dev/mapper/root /mnt/var
# mount -o subvol=@data --mkdir /dev/mapper/root /mnt/data

# mount --mkdir /dev/disk/by-partlabel/efipart /mnt/efi

Install Base Pkgs

Ref: Installation guide#Install essential packages

Kernel

CPU microcode updates "amd-ucode" or "intel-ucode" for hardware bug and security fixes:

# pacstrap -K /mnt \
    base linux linux-lts linux-firmware btrfs-progs iptables-nft \
    amd-ucode neovim

"-K" means to initialize an empty pacman keyring in the target, so only adding it at first running.
Ref: pacstrap(8)

The latest kernel sometimes may cause annoying bugs like this, so setting LTS kernel as a fallback option would be a good choice.

Swap on Zram

Ref: Zram#Using zram-generator

# pacstrap /mnt zram-generator

Create "/mnt/etc/systemd/zram-generator.conf" with:

[zram0]
zram-size = min(ram, 8192)
compression-algorithm = zstd

Sudo

Ref: Sudo#Environment variables , Sudo#Example entries , Sudo#Tips and tricks

# pacstrap /mnt sudo bash-completion

Create "/mnt/etc/sudoers.d/sudoers" with:

Defaults passwd_timeout = 0
Defaults timestamp_type = global
Defaults timestamp_timeout = 15
Defaults editor = /usr/bin/nvim
%wheel ALL=(ALL:ALL) ALL

Console Font

# pacstrap /mnt terminus-font
# echo "FONT=ter-132b" >> /mnt/etc/vconsole.conf

Utilities

# pacstrap /mnt \
    base-devel pacman-contrib \
    man-db man-pages texinfo \
    iwd git rsync

iwd is a wireless network management tool.

Fstab

When using encrypted containers with dm-crypt, the labels of filesystems inside of containers are not available while the container is locked/encrypted.
Ref: Persistent block device naming#by-label

The advantage of using the UUID method is that it is much less likely that name collisions occur than with labels. Further, it is generated automatically on creation of the filesystem. It will, for example, stay unique even if the device is plugged into another system (which may perhaps have a device with the same label).
Ref: Persistent block device naming#by-uuid

It is preferable to mount using subvol=/path/to/subvolume, rather than the subvolid, as the subvolid may change when restoring snapshots, requiring a change of mount configuration.
Ref: Btrfs#Mounting subvolumes

Since "genfstab" would generate subvolid and other redundant options, I choose to write fstab manually.

Save partitions UUID to temp files for later use:

# blkid -s UUID -o value /dev/vda1 > /tmp/efiuuid
# blkid -s UUID -o value /dev/mapper/root > /tmp/luksuuid

Edit "/mnt/etc/fstab" with:

UUID=XXXX-XXXX /efi vfat defaults 0 2
UUID=xxxxxxxx-...-xxxxxxxxxxxx / btrfs compress=zstd,subvol=/@ 0 0
UUID=xxxxxxxx-...-xxxxxxxxxxxx /home btrfs compress=zstd,subvol=/@home 0 0
UUID=xxxxxxxx-...-xxxxxxxxxxxx /var btrfs compress=zstd,subvol=/@var 0 0
UUID=xxxxxxxx-...-xxxxxxxxxxxx /data btrfs compress=zstd,subvol=/@data 0 0

Networking

Systemd-networkd

Ref: Network configuration#Network management

“For system with multiple network interfaces that are not expected to be connected all the time (e.g. if a dual-port Ethernet card, but only one cable plugged in), starting systemd-networkd-wait-online.service will fail after the default timeout of 2 minutes. This may cause an unwanted delay in the startup process. To change the behaviour to wait for any interface rather than all interfaces to become online, edit the service and add the –any parameter to the ExecStart line:”

# _waitdir=/mnt/etc/systemd/system/systemd-networkd-wait-online.service.d
# mkdir -p ${_waitdir}
# cat > ${_waitdir}/wait-for-only-one-interface.conf <<EOB
[Service]
ExecStart=
ExecStart=/usr/lib/systemd/systemd-networkd-wait-online --any
EOB

Ref: Systemd-networkd#systemd-networkd-wait-online

Run ip link show to list network devices, write configs for them:

# cat > /mnt/etc/systemd/network/23-enp0s1.network <<EOB
[Match]
Name=enp0s1
[Link]
RequiredForOnline=routable
[Network]
DHCP=yes
[DHCPv4]
RouteMetric=100
[IPV6AcceptRA]
RouteMetric=100
EOB

# cat > /mnt/etc/systemd/network/27-wireless.network <<EOB
[Match]
Name=wlan0
[Link]
RequiredForOnline=routable
[Network]
DHCP=yes
IgnoreCarrierLoss=3s
[DHCPv4]
RouteMetric=600
[IPV6AcceptRA]
RouteMetric=600
EOB

Ref: Systemd-networkd#Wired and wireless adapters on the same machine

“IgnoreCarrierLoss=3s ensures that systemd-networkd will not re-configure the interface (e.g., release and re-acquire a DHCP lease) for a short period (3 seconds in this example) while the wireless interface roams to another access point within the same wireless network (SSID), which translates to shorter downtime when roaming.”

Ref: Systemd-networkd#Wireless adapter

If you have multiple network cards of same type, say 2 wired interfaces, you must specific different RouteMetric for them, or the “race condition” will cause extreamly slow network connections.

Ref: Systemd-networkd#Prevent multiple default routes

If you have custom routing configs, you must disable ManageForeignRoutingPolicyRules option, because it will remove rules that are not configured in .network files by default.

“systemd-networkd will alter routing tables also for other network software. If this is undesired, configure ManageForeignRoutingPolicyRules= in networkd.conf(5) accordingly. For example, see WireGuard#Connection lost after sleep using systemd-networkd

# sed -i '/^\[Network/a\ManageForeignRoutingPolicyRules=no' \
    /mnt/etc/systemd/networkd.conf

Ref: Systemd-networkd#Configuration files

Enable systemd-networkd.service:

# systemctl enable systemd-networkd.service --root=/mnt

Systemd-resolved

Enable systemd-resolved.service to let DNS work:

# systemctl enable systemd-resolved.service --root=/mnt

Ref: Systemd-resolved

Chroot

# arch-chroot /mnt

Time:

# ln -sf /usr/share/zoneinfo/Asia/Shanghai /etc/localtime
# hwclock --systohc
# systemctl enable systemd-timesyncd.service

Localization:

# echo "en_US.UTF-8 UTF-8" >> /etc/locale.gen
# echo "zh_CN.UTF-8 UTF-8" >> /etc/locale.gen
# locale-gen
# echo "LANG=en_US.UTF-8" >> /etc/locale.conf

Hostname:

# echo "archlinux" >> /etc/hostname

Remap CapsLock to Control for console.
Ref: Linux console/Keyboard configuration#Creating a custom keymap

# _kmapdir=/mnt/usr/share/kbd/keymaps/i386/qwerty
# gzip -dc < ${_kmapdir}/us.map.gz > ${_kmapdir}/usa.map
# sed -i '/^keycode[[:space:]]58/c\keycode 58 = Control' \
    ${_kmapdir}/usa.map
# echo "KEYMAP=usa" >> /mnt/etc/vconsole.conf

Enable multilib repo. Ref: General recommendations#Repositories

# cat >> /mnt/etc/pacman.conf <<EOB
[multilib]
Include = /etc/pacman.d/mirrorlist
EOB

Root Password:

# passwd

Create User:

# useradd -m -G wheel -s /bin/bash user1
# passwd user1

Initramfs

Ref: dm-crypt/System configuration#mkinitcpio

Edit "/etc/mkinitcpio.conf":

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

Recreate initramfs image:

# mkinitcpio -P

Systemd-boot

Install UEFI boot manager.
Ref: Systemd-boot#Installing the UEFI boot manager , Systemd-boot#systemd service

bootctl install
systemctl enable systemd-boot-update.service

Boot Files

Copy boot files to ESP.
Ref: EFI system partition#Alternative mount points

# mkdir -p /efi/EFI/arch
# cp -a /boot/vmlinuz-linux /efi/EFI/arch/
# cp -a /boot/initramfs-linux.img /efi/EFI/arch/
# cp -a /boot/vmlinuz-linux-lts /efi/EFI/arch/
# cp -a /boot/initramfs-linux-lts.img /efi/EFI/arch/

Auto update boot files under ESP with systemd.
Ref: EFI system partition#Using systemd , systemd.path(5)

Create "/etc/systemd/system/efistub-update.path"

[Unit]
Description=Copy EFISTUB Kernel to EFI system partition
[Path]
PathChanged=/boot/initramfs-linux-fallback.img
[Install]
WantedBy=multi-user.target
WantedBy=system-update.target

Create "/etc/systemd/system/efistub-update.service"

[Unit]
Description=Copy EFISTUB Kernel to EFI system partition
[Service]
Type=oneshot
ExecStart=/usr/bin/cp -af /boot/vmlinuz-linux /efi/EFI/arch/
ExecStart=/usr/bin/cp -af /boot/initramfs-linux.img /efi/EFI/arch/
ExecStart=/usr/bin/cp -af /boot/vmlinuz-linux-lts /efi/EFI/arch/
ExecStart=/usr/bin/cp -af /boot/initramfs-linux-lts.img /efi/EFI/arch/

Enable systemd units:

# systemctl enable efistub-update.{path,service}

Boot Loader

Ref: Systemd-boot#Configuration

Edit "/efi/loader/loader.conf":

default arch.conf
timeout 0
console-mode max
editor no

"timeout 0" means not showing menu and boot immediately.

Create "/efi/loader/entries/arch.conf".

title Arch Linux
linux /EFI/arch/vmlinuz-linux
initrd /EFI/arch/initramfs-linux.img
options rootflags=subvol=@ quiet

To use a subvolume as the root mountpoint, specify the subvolume via a kernel parameter using rootflags=subvol=@. Or you would get an error “Failed to start Switch Root” when booting.
Ref: Btrfs#Mounting subvolume as root

Create "/efi/loader/entries/arch-lts.conf".

title Arch Linux LTS
linux /EFI/arch/vmlinuz-linux-lts
initrd /EFI/arch/initramfs-linux-lts.img
options rootflags=subvol=@ quiet

Note: If disk partitions were not following Discoverable Partitions Specification , which means root partition would not be discovered and auto mounted, booting system would stuck at "a start job is running for /dev/gpt-auto-root" and timeout. To fix this, specify root partition in kernel parameters.
Ref: dm-crypt/Encrypting an entire system#Configuring the boot loader,
dm-crypt/System configuration#rd.luks.name

options rd.luks.name=<UUID>=root root=/dev/mapper/root rootflags=subvol=@ quiet

Move PacmanDB

The pacman database in /var/lib/pacman must stay on the root subvolume @.
Ref: Snapper#Suggested filesystem layout

So, to keep /var as a separate btrfs subvolume, we need to move pacman database out of /var:

# sed -i '/^#DBPath/a\DBPath=/usr/pacmandb' /etc/pacman.conf
# mv /var/lib/pacman /usr/pacmandb

Disable Watchdogs

# cat > /mnt/etc/modprobe.d/nowatchdogs.conf <<EOB
blacklist iTCO_wdt
blacklist sp5100_tco
EOB

Ref: Improving performance#Watchdogs , Kernel module#Blacklisting

Reboot

# exit
# reboot

In case you’ve lost track of the installation process and want to start over, here’s some commands that can help you reset disk state, then you can retry installation.
Ref: dm-crypt/Drive preparation#Wipe LUKS header

# umount /mnt/efi
# umount -AR /mnt
# cryptsetup close /dev/mapper/root
# cryptsetup erase /dev/vda2
# wipefs -a /dev/vda