Arch Linux install guide 2022 Edition

Posted on Fri 24 December 2021 in software

Introduction

This is the way I usually install Arch Linux. It is heavily based on systemd services to avoid the installation of additional software. Use it as inspiration for your own custom install.

Arch Linux is all about freedom of choice so these are the choices I made:

Component Implementation
Kernel Default linux
Partition layout GPT
Bootloader systemd-boot
Initramfs systemd based mkinitcpio
Partitioning Primary partitions, single partition for root and home
Encryption LUKS2 for swap and rootfs
Root filesystem Ext4
Swap space Dedicated partition
Network manager systemd-networkd
Wifi wpa_supplicant
Mobile broadband Custom modem-manager script
Name resolution systemd-resolved
Network Time systemd-timesyncd
Text Editor Neovim (nvim)
User shell fish
/bin/sh Shell dash
Privilege excalation sudo
Desktop Environment xfce
Window Manager i3
Terminal xfce-terminal
Keyboard layout en-us international with AtlGr dead keys; caps lock as compose key

Explanation for code examples:

# command executed as root
$ command executed as unprivileged user
% command executed inside chroot as root

Assumptions

  • You install to a notebook or desktop computer (ignore battery related things)
  • You install to an NVME drive
  • You read through my commands carefully and understand what they do before blindly pasting them into your machine
  • I’m not responsible for anything you do

With that out of the way, let’s get started!

Installation

Partition disks

Create 3 primary partitions for:

  • ESP: 1GB, FAT32
  • Swap:
    • With hibernation support: same as RAM size
    • Without: ~2-8GB depending on the use case of the machine
  • RootFS: Rest of your disk
# cfdisk -z /dev/nvme0n1
# mkdosfs -F 32 -n ESP /dev/nvme0n1p1
# cryptsetup LuksFormat --type luks2 --label CRYPTSWAP /dev/nvme0n1p2
# cryptsetup LuksFormat --type luks2 --label CRYPTROOT /dev/nvme0n1p3
# cryptsetup open --allow-discards --perf-no_read_workqueue --perf-no_write_workqueue --persistent --type luks2 /dev/nvme0n1p2 cryptswap
# cryptsetup open --allow-discards --perf-no_read_workqueue --perf-no_write_workqueue --persistent --type luks2 /dev/nvme0n1p3 cryptroot
# mkswap -L SWAP /dev/mapper/cryptswap
# mkfs.ext4 -L ROOT -O metadata_csum /dev/mapper/cryptroot
# mount /dev/mapper/cryptroot /mnt -o noatime,discard
# mkdir /mnt/boot
# mount /dev/nvme0n1p1 /mnt/boot -o noatime,discard
# swapon -d /dev/mapper/cryptswap

Final result should look like this:

# lsblk
NAME          MAJ:MIN RM   SIZE RO TYPE  MOUNTPOINTS
nvme0n1       259:0    0 953.9G  0 disk
├─nvme0n1p1   259:1    0     1G  0 part  /boot
├─nvme0n1p2   259:2    0    32G  0 part
 └─cryptswap 254:1    0    32G  0 crypt [SWAP]
└─nvme0n1p3   259:3    0 920.9G  0 part
  └─cryptroot 254:0    0 920.9G  0 crypt /

Install base system

Some packages mentioned here depend on your hardware

# pacstrap /mnt base base-devel linux linux-firmware (intel-ucode|amd-ucode) [sof-firmware] neovim man-db man-pages texinfo

Generate fstab

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

Setup bootloader

Get UUIDs of drives:

# blkid
/dev/nvme0n1p1: LABEL_FATBOOT="ESP" LABEL="ESP" UUID="1234-0001" BLOCK_SIZE="512" TYPE="vfat" PARTUUID="deadbeef-1234-5678-90ab-10000001"
/dev/nvme0n1p2: UUID="deadbeef-1234-5678-90ab-00000002" LABEL="CRYPTSWAP" TYPE="crypto_LUKS" PARTUUID="deadbeef-1234-5678-90ab-10000002"
/dev/nvme0n1p3: UUID="deadbeef-1234-5678-90ab-00000003" LABEL="CRYPTROOT" TYPE="crypto_LUKS" PARTUUID="deadbeef-1234-5678-90ab-20000003"
/dev/mapper/cryptswap: LABEL="SWAP" UUID="deadbeef-1234-5678-90ab-20000002" TYPE="swap"
/dev/mapper/cryptroot: LABEL="ROOT" UUID="deadbeef-1234-5678-90ab-20000003" BLOCK_SIZE="4096" TYPE="ext4"

Install and configure systemd-boot:

# arch-chroot /mnt  # we'll stay in chroot for a while so I'll not prefix every code block with that
% bootctl install
% nvim /boot/loader/loader.conf
default arch.conf
timeout 3
console-mode keep
editor yes
auto-entries
auto-firmware

Setup boot entries:

% nvim /boot/loader/entries/arch.conf
title   Arch Linux
linux   /vmlinuz-linux
initrd  /intel-ucode.img
initrd  /initramfs-linux.img
options root="UUID=deadbeef-1234-5678-90ab-20000003" resume="UUID=deadbeef-1234-5678-90ab-20000002" rd.luks.name=deadbeef-1234-5678-90ab-00000003=cryptroot rd.luks.name=deadbeef-1234-5678-90ab-00000002=cryptswap rd.luks.options=discard rw

% nvim /boot/loader/entries/arch-fallback.conf
title   Arch Linux (fallback initramfs)
linux   /vmlinuz-linux
initrd  /intel-ucode.img
initrd  /initramfs-linux-fallback.img
options root="UUID=deadbeef-1234-5678-90ab-20000003" resume="UUID=deadbeef-1234-5678-90ab-20000002" rd.luks.name=deadbeef-1234-5678-90ab-00000003=cryptroot rd.luks.name=deadbeef-1234-5678-90ab-00000002=cryptswap rd.luks.options=discard rw

Setup vconsole

% nvim /etc/vconsole.conf
KEYMAP=us
FONT=lat2-16
FONT_MAP=8859-2

Setup initramfs

Switching to systemd based initramfs:

% nvim /etc/mkinitcpio.conf
[...]
HOOKS=(base systemd autodetect keyboard sd-vconsole modconf block sd-encrypt filesystems fsck)
[...]

% mkinitcpio -P

Setup networking

Setup for wired connections:

% nvim /etc/systemd/network/wired.network
[Match]
Name=enp*

[Network]
DHCP=yes
MulticastDNS=resolve
DNSSEC=allow-downgrade

[DHCP]
RouteMetric=10

Setup for wireless connections:

% nvim /etc/systemd/network/wireless.network
[Match]
Name=wlp*

[Network]
DHCP=yes
MulticastDNS=resolve
DNSSEC=allow-downgrade
IPv6PrivacyExtensions=kernel

[DHCP]
RouteMetric=20

Enable IPv6 privacy extensions:

Execute this first:

% sysctl net.ipv6.conf.wlp3s0.addr_gen_mode=3
% ip link set wlp3s0 down
% ip link set wlp3s0 up
% sysctl net.ipv6.conf.wlp3s0.stable_secret

Take the secret from the last command and put it here:

% nvim /etc/sysctl.d/40-ipv6.conf
# Enable IPv6 Privacy Extensions
net.ipv6.conf.all.use_tempaddr = 2
net.ipv6.conf.default.use_tempaddr = 2
net.ipv6.conf.wlp3s0.use_tempaddr = 2
net.ipv6.conf.enp5s0.use_tempaddr = 2

# Enable IPv6 stable privacy mode
net.ipv6.conf.wlp3s0.stable_secret = 25a3:36e6:9384:1111:9866:def1:abcd:1234
net.ipv6.conf.wlp3s0.addr_gen_mode = 2

Install additional packages for wifi and enable daemon on your wifi interface:

$ yay -S wpa_supplicant
# systemdctl enable wpa_supplicant@wlp3s0.service

Setup for mobile broadband follows later.

% systemctl enable systemd-networkd
% systemctl enable systemd-resolved
% rm /etc/resolv.conf
% ln -sf /run/systemd/resolve/stub-resolv.conf /etc/resolv.conf

The last command will break your DNS but that’s fine because we will reboot in a moment. Maybe you have to do this after reboot. Definitly check if it worked.

Set hostname

% echo "myhostname" > /etc/hostname

Setup network time and timezone

% nvim /etc/systemd/timesyncd.conf
[Time]
NTP=0.arch.pool.ntp.org 1.arch.pool.ntp.org 2.arch.pool.ntp.org 3.arch.pool.ntp.org
FallbackNTP=0.de.pool.ntp.org 1.de.pool.ntp.org 2.de.pool.ntp.org 3.de.pool.ntp.org

% systemctl enable systemd-timesyncd
% ln -sf /usr/share/zoneinfo/Europe/Berlin /etc/localtime
% hwclock --systohc

Locale setup

Edit /etc/locale.gen file in your chroot and uncomment en_US.UTF-8 UTF-8 locales. Then run:

% locale-gen
% nvim /etc/locale.conf
LANG=en_US.UTF-8
LANGUAGE=en_US:en
LC_COLLATE=C

Set root password

% passwd

Reboot into new system

% exit  # exit chroot
# umount -R /mnt
# swapoff -a
# cryptsetup close cryptroot
# cryptsetup close cryptswap
# reboot

Check if time is correct

# timedatectl status
               Local time: Thu 2021-08-05 18:08:59 CEST
           Universal time: Thu 2021-08-05 16:08:59 UTC
                 RTC time: Thu 2021-08-05 16:09:09
                Time zone: Europe/Berlin (CEST, +0200)
System clock synchronized: no
              NTP service: inactive
          RTC in local TZ: no

Enable synchronisation with:

# timedatectl set-ntp true

Create unprivileged user

# pacman -Syu sudo fish python pkgfile
# useradd -m -G wheel -s /usr/bin/fish bob
# EDITOR=nvim visudo
[...]
%wheel ALL=(ALL) ALL
[...]

# passwd bob

Setup AUR

Use unprivileged user from here on

$ sudo pacman -S git
$ git clone https://aur.archlinux.org/yay.git
$ cd yay
$ makepkg -csi
$ cd ..
$ rm -fr yay

Some quality of live things

Update systemd-boot on ESP when systemd is updated

$ yay -S systemd-boot-pacman-hook

System monitoring with temperatures

$ yay -S htop lsof strace lm_sensors
# sensors-detect  # hit enter until it stops

Use dash for /bin/sh

This could cause problems, use checkbashisms to check your scripts:

$ yay -S checkbashisms
$ find /usr/bin/ -type f -perm -o=r -print0 | xargs -0 gawk '/^#!.*( |[/])sh/{printf "%s\0", FILENAME} {nextfile}' | xargs -0 checkbashisms
$ yay -S dashbinsh

Make pacman nice and fast

Edit /etc/pacman.conf and add/change these lines:

Color
VerbosePkgLists
ParallelDownloads = 10
ILoveCandy

Also enable multilib:

[multilib]
Include = /etc/pacman.d/mirrorlist

Grafical setup

Setup gdm

$ yay -S gdm
# systemctl enable gdm.service

Setup XFCE with i3 as window manager

Install packages first. Choose as many from these groups as you like. You can probably skip most panel plugins:

$ yay -S xfce4 xfce4-goodies

My favorites even under i3 are: xfce4-screenshooter, xfce4-terminal, thunar

For thunar setup gvfs:

$ yay -S gvfs gvfs-afc gvfs-goa gvfs-google gvfs-gphoto2 gvfs-mtp gvfs-nfs gvfs-smb

Install i3 (or i3-gaps if you wanna waste precious space just for looks):

$ yay -S i3 i3blocks j4-dmenu-desktop

Switch to i3 as window manager:

xfconf-query -c xfce4-session -p /sessions/Failsafe/Client0_Command -t string -sa xfsettingsd
xfconf-query -c xfce4-session -p /sessions/Failsafe/Client1_Command -t string -sa i3

This should change the value from xfwm4 to i3 in ~/.config/xfce4/xfconf/xfce-perchannel-xml/xfce4-session.xml.

Fonts

% yay -S ttf-droid ttf-dejavu \  # default stuff
noto-fonts noto-fonts-cjk noto-fonts-emoji noto-fonts-extra \  # emoji
adobe-source-code-pro-fonts \  # monospace
adobe-source-sans-fonts \  # sans-serif
adobe-source-han-sans-cn-fonts adobe-source-han-sans-tw-fonts adobe-source-han-serif-cn-fonts adobe-source-han-serif-tw-fonts \  # chinese
adobe-source-han-sans-jp-fonts adobe-source-han-serif-jp-fonts \  # japanese
adobe-source-han-sans-kr-fonts  # korean

Bonus!

Optimize pacman/yay

$ yay -S ccache

In /etc/makepkg.conf:

  • For CFLAGS change -march=native and -mtune=native.
  • Set MAKEFLAGS="-j8" or any othe rnumber of cores you want to use
  • For BUILDENV enable ccache

Wayland

Enable wayland support for Qt5/6

$ yay -S qt5-wayland qt6-wayland glfw-wayland

Enable wayland support for electron apps by editing ~/.config/electron-flags.conf:

--enable-features=UseOzonePlatform
--ozone-platform=wayland

Install XWayland to support X11 applications:

$ yay -S xorg-xwayland

Sway

TBC

yay -S sway \
    xdg-desktop-portal xdg-desktop-portal-wlr \  # screen sharing/webrtc

Gnome

Simply install the gnome group which includes most gnome packages you’ll need:

$ yay -S gnome gnome-tweaks webp-pixbuf-loader

After a few hundred lines of things scroll by this the stuff that was too fast to read:

Install these packages that add features you propably want:

$ yay -S qt5-svg \  # svg icon set support
    vulkan-mesa-layers \  # vulkan support
    vulkan-intel \  # vulkan support (intel)
    gst-libav gst-plugins-ugly \  # media file support
    intel-media-driver libva-intel-driver \  # media file support (intel)
    eog-plugins \  # image support for eye of gnome
    poppler-data \  # pdf feature support
    p7zip unrar \  # file-roller archive support
    gedit-plugins \
    fwupd dmidecode \  # firmware update support
    pipewire-alsa pipewire-pulse pipewire-zeroconf \  # pulseaudio replacement
    xdg-desktop-portal xdg-desktop-portal-gtk \  # screen sharing/webrtc
    gtk-engines \  # gtk2 themes
    chromium \  # browser
    evolution evolution-ews highlight \  # email client

Enable pipewire support in chromium by enabling: chrome://flags/#enable-webrtc-pipewire-capturer