Jake Scott

Arch Install with Drive Encryption, BTRFS, and ZRAM

Mar 1, 2024

I tend to use Fedora as my daily OS, but every once in a while I get the itch to check out Arch again. This is my collection of arch install notes I’ve accumulated over the years. Following these notes will get you a base Arch install with LUKS encryption, BTRFS, and zram swap enabled. I’ve collected all this into an Arch Install Script. The script just runs the steps outlined below. See the Arch Install Guide for more details about the install process.

Keep in mind these notes are based around a UEFI system with secure boot turned off. There is now also an official install script Archinstall. Probabably worth using that over my janky one.

Basic Install

First start by syncing the system clock with

timedatectl set-ntp true

Partitions

You can then partition the disks. I tend to like to keep it simple with 2 partitions, 1 for boot and 1 for root respectively. Your device will be something like /dev/sda. You can run lsblk to see a list of your devices.

parted --script "${device}" mklabel gpt \
    mkpart efi fat32 1Mib 261MiB \
    set 1 boot on \
    mkpart system btrfs 261MiB 100%

Encryption Setup & Formatting

This is an overview for setting up LUKS on a partition. The parted command from the previous section applied labels to the partitions for easy reference. Run the following commands to encrypt the root partition.

mkfs.fat -F32 -n EFI /dev/disk/by-partlabel/efi
cryptsetup -y -v luksFormat /dev/disk/by-partlabel/system
cryptsetup open /dev/disk/by-partlabel/system root
mkfs.btrfs -f /dev/mapper/root

Create BTRFS subvolumes

Subvolumes allow for snapshots and rollbacks. There are several possible subvolume layouts, this is what I’ve settled on based on archinstaller:

mount -t btrfs /dev/mapper/root /mnt
btrfs subvolume create /mnt/@
btrfs subvolume create /mnt/@home
btrfs subvolume create /mnt/@log
btrfs subvolume create /mnt/@pkg
btrfs subvolume create /mnt/@snapshots
btrfs subvolume set-default <subvol-id-of-@> /mnt
umount -R /mnt

Mount files systems

Now we need to mount the file system to prep for installing Arch. I’ve chosen noatime and compress=zstd mount options based on some recommendations from the original btrfs website.

m_opts=defaults,noatime,compress=zstd
mount btrfs -o $m_opts,subvol=@ /dev/mapper/root /mnt
mount --mkdir -t btrfs -o $m_opts,subvol=@home /dev/mapper/root /mnt/home
mount --mkdir -t btrfs -o $m_opts,subvol=@snapshots /dev/mapper/root /mnt/.snapshots
mount --mkdir -t btrfs -o $m_opts,subvol=@log /dev/mapper/root /mnt/var/log
mount --mkdir -t btrfs -o $m_opts,subvol=@pkg /dev/mapper/root /mnt/var/cache/pacman/pkg
mount --mkdir -t btrfs -o $m_opts,subvolid=5 /dev/mapper/root /mnt/btrfs
mount --mkdir LABEL=EFI /mnt/boot

Install Packages

Update the arch mirror list with reflector. This will get you a list of your fastest mirrors.

reflector --latest 5 --sort rate --country US --save /etc/pacman.d/mirrorlist

Now that the drives are mounted and mirrors updated you can run pacstrap to install the base packages and kernel. I’ve go a selection of packages I like here, but feel free to change it up. Select the microcode appropriate to your system, amd-ucode or intel-ucode.

pacstrap -K /mnt base base-devel linux linux-firmware linux-headers \
    e2fsprogs btrfs-progs exfat-utils efibootmgr man-db man-pages \
    texinfo cryptsetup networkmanager sudo ufw neovim git ${cpu}-ucode \
    reflector

It may be worth trying linux-zen if you experience UI hangs during high CPU load. Although the performance benefits are dubious. An analysis here.

Remember to install microcode, amd-ucode or intel-ucode respectively. It’s important.

Configuration Stuff

Generate fstab:

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

Chroot into the new system:

arch-chroot /mnt

Set the time zone:

ln -sf /usr/share/zoneinfo/${region}/${city} /etc/localtime

Run hwclock to generate /etc/adjtime:

hwclock --systohc

Uncomment en_US.UTF-8 UTF-8 in /etc/locale.gen then generate the locales:

sed -d `s/#en_US.UTF-8/en_US.UTF-8/` /etc/locale.gen
locale-gen

Create /etc/locale.conf and set the LANG variable:

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

Create /etc/hostname with the name of this computer and populate the /etc/hosts file:

echo "${hostname}" > /etc/hostname
cat > /mnt/etc/hosts << EOF
127.0.0.1     localhost
::1           localhost
127.0.1.1     ${hostname}.localdomain ${hostname}
EOF

Refresh initramfs, be sure to add encrypt to /etc/mkinitcpio.conf:

# /etc/mkinitcpio.conf
...
HOOKS=(base udev ... block encrypt filesystems ...)
...
mkinitcpio -P

Set the root password:

passwd

Enable Network Manager and Firewall:

systemctl enable NetworkManager.service
systemctl enable ufw.service

Remember to run ufw enable after re-booting into the system.

Setup Swap with zram

Instead of a swap partition which takes disk space and has some encryption considerations you can use zram. Enabling zram swaps programs into a compressed area of ram, instead of a dedicated partition.

You can activate zram at boot with a manually defined udev rule. Alternatively, you can use zram-generator.

Here are the steps for the udev rule method.

Load module at boot:

# /etc/modules-load.d/zram.conf
zram

Create udev rule, adjust disksize as necessary. 4GB seems common, although the standard $swap = \sqrt{ram}$ still seems to hold.

# /etc/udev/rules.d/99-zram.rules
ACTION=="add", KERNEL=="zram0", ATTR{comp_algorithm}="zstd", ATTR{disksize}="4G", RUN="/usr/bin/mkswap -U clear /dev/%k", TAG+="systemd"

Add /dev/zram device to fstab.

# /etc/fstab
/dev/zram0 none swap defaults,pri=100 0 0

zswap and zram are mutually exclusive. Be sure to disable zswap with the zswap.enabled=0 kernel parameter. I have this set in the next section.

zram will not work with hibernate. Sleep should still work.

Setup Boot Loader

I like to use systemd-boot since it’s included in the base arch install.

Install and update into boot partition:

bootctl --path=/boot install
bootctl update

Create a pacman hook update boot partition when systemd-boot updates:

mkdir -p /etc/pacman.d/hooks
cat > /etc/pacman.d/hooks/100-systemd-boot.hook << EOF
[Trigger]
Type = Package
Operation = Upgrade
Target = systemd

[Action]
Description = Updating systemd-boot.
When = PostTransaction
Exec = /usr/bin/systemctl restart systemd-boot-update.service
EOF

If you at some point enable secure boot see here for a hook to sign the new boot manager.

Create boot config files:

cat > /boot/loader/loader.conf << EOF
default arch
timeout 0
console-mode max
editor no
EOF

cat > /boot/loader/entries/arch.conf << EOF
title Arch Linux
linux /vmlinuz-linux
initrd /${cpu}-ucode.img
initrd /initramfs-linux.img
options cryptdevice=UUID=$(blkid -s UUID -o value /dev/disk/by-partlabel/system):root root=/dev/mapper/root rw quiet splash zswap.enabled=0
EOF

You can get <device-UUID> with blkid /dev/<partname> and grab the top-level encrypted partition

Create an Admin User

Create an admin user and edit sudoers to allow sudo privileges for wheel group.

useradd -mU -G wheel "${username}"
echo "$username:$password" | chpasswd
sed -i 's/# %wheel ALL=(ALL) ALL/%wheel ALL=(ALL) ALL/' /etc/sudoers

You should now be ready to exit the chroot, unmount the filesystem, and reboot the system.

exit
umount -R /mnt
reboot

Remove the install media and log in to your fresh system.

Now you should have a minimal Arch system with btrfs, encrypted root, systemd-boot and zram swap. You’re free to stop here and officially be able to tell everyone you use Arch… BTW.

Post-install Stuff

Here are some nice-to-haves for setting up the rest of the desktop system.

Disable root Account

I like to have root login disabled for some extra security.

passwd -l root

Make sure sudo works for you user before doing this.

Disable Terminal Bell

Die terminal bell, die!

rmmod pcspkr
echo "blacklist pcspkr" > /etc/modprobe.d/nobeep.conf

Optimize swappiness for zram

Generally vm.swappiness=10 is a common recommendation for high-ram systems. However, zram has very low IO swap cost so these settings provide a little more optimization. For zram, swappiness of 100 is appropriate (100 indicates no extra IO cost)

cat > /etc/sysctl.d/99-vm-zram-parameters.conf << EOF
vm.swappiness = 100
vm.watermark_boost_factor = 0
vm.watermark_scale_factor = 125
vm.page-cluster = 0
EOF

Make pacman pretty and fast

This will enable color output and parallel packages downloads.

sed -i "s/^#Color/Color/" /etc/pacman.conf
sed -i "s/^#ParallelDownloads/ParallelDownloads/" /etc/pacman.conf

It can also be worth setting up a schedule to clear the package cache. I tend to just do it manually occasionally.

Audio

Install pipewire and associated packages. This gives you a modern Linux audio stack.

sudo pacman -S pipewire pipewire-docs pipewire-audio pipewire-pulse pipewire-jack wireplumber
systemctl --user enable --now pipewire
systemctl --user enable --now pipewire-pulse

You can then use wpctl to manage sources and sinks.

Make sure you run these commands as your user.

Display drivers

Drivers are dependent on what GPU you have. Take a look at the wiki article for display driver installation.

I have an AMD card, so I install:

pacman -S xf86-video-amdgpu mesa vulkan-radeon libva-mesa-driver libva-utils 

If you’re using multilib (e.g. with Steam) you will also want lib32-mesa and lib32-vulkan-radeon.

Fonts

For decent font coverage I install

Gnome

I am an unabashed Gnome enjoyer. This will install Gnome and enable gdm at next boot.

pacman -S gnome gnome-shell-extension-appindicator
sysetmctl enable gdm

Printing and .local discovery

Enable the avahi-daemon to allow .local domain device discovery.

systemctl enable --now avahi-daemon.service

You’ll also need cups for printing and cups-pdf if you want to be able to print to pdf.

pacman -S cups cups-pdf
systemctl enable cups.socket