rss logo

Comprehensive ArchLinux Installation Guide (UEFI + Btrfs + Encryption)

ArchLinux logo

I will describe how I install ArchLinux on my personal desktop. Hopefully, this guide will be useful to both others and yourself!

However, for servers, I prefer Debian over ArchLinux. But that's a different topic altogether.

Note: This tutorial draws significant inspiration from the official installation guide: https://wiki.archlinux.org.

Installation medium

  • First, download the ISO media from the official download webpage (refer to here to verify the signature):

https://archlinux.org/download/

  • Identify the USB media:
[root@host ~]# fdisk -l
  • Personally, I create a USB boot media:
[root@host ~]# dd if=archlinux-XXXX.XX.XX-x86_64.iso of=/dev/sdX bs=16M status=progress; sync
  • Disable UEFI secure boot (since the installation media does not support this feature) and boot from the USB drive.

Pre-Install

  • Set the console keyboard layout (e.g., fr for French, us for US, de for German, etc.):
root@archiso ~ # loadkeys fr
  • Verify that we are in UEFI boot mode:
root@archiso ~ # if [ -d /sys/firmware/efi/efivars ]; then echo "UEFI OK"; else echo "UEFI KO"; fi

Network Configuration

  • List and identify network interfaces:
root@archiso ~ # ip link
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN mode DEFAULT group default qlen 1000
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
2: ens192: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc mq state UP mode DEFAULT group default qlen 1000
    link/ether 00:50:56:80:0b:32 brd ff:ff:ff:ff:ff:ff
    altname enp11s0
  • Obtain network configuration via DHCP:
root@archiso ~ # dhclient ens192
  • Or set IP manually:
root@archiso ~ # ip addr add 192.168.1.10/24 dev ens192
root@archiso ~ # ip route add default via 192.168.1.254
root@archiso ~ # echo 'nameserver 192.168.1.254' >> /etc/resolv.conf
  • Check internet connectivity:
root@archiso ~ # ping 46.105.57.169
  • Check name resolution:
root@archiso ~ # host std.rocks
std.rocks has address 46.105.57.169
std.rocks has IPv6 address 2001:41d0:301::20
std.rocks mail is handled by 1 mx4.mail.ovh.net.
std.rocks mail is handled by 10 mx3.mail.ovh.net.
Note: From this step, if necessary, you can log in to the installation program as root via SSH from another machine. Just use the "passwd" command beforehand to set a password.
  • Update the system clock:
root@archiso ~ # timedatectl set-ntp true

Partition the disk

I will partition my 40GB disk as follows:

Partition Note Filesystem Size
EFI EFI partition FAT32 512M
/boot Boot partition ext2 500M
swap Swap partition Swap on LUKS 2G
/ Root partition Btrfs on LUKS 37G
  • The graphical view of the disk partitioning:
Graphical representation of ArchLinux disk partitioning with EFI, boot, swap, and encrypted root partitions using Btrfs and LUKS encryption.

⚠️ Please note that the sda disk will be completely erased, resulting in data loss. ⚠️

  • Identify the destination disk:
root@archiso ~ # fdisk -l
Disk /dev/sda: 40 GiB, 42949672960 bytes, 83886080 sectors
Disk model: Virtual disk    
Units: sectors of 1 * 512 = 512 bytes
Sector size (logical/physical): 512 bytes / 512 bytes
I/O size (minimum/optimal): 512 bytes / 512 bytes


Disk /dev/loop0: 683.24 MiB, 716427264 bytes, 1399272 sectors
Units: sectors of 1 * 512 = 512 bytes
Sector size (logical/physical): 512 bytes / 512 bytes
I/O size (minimum/optimal): 512 bytes / 512 bytes
  • Start partitioning, using the /dev/sda device:
root@archiso ~ # gdisk /dev/sda
GPT fdisk (gdisk) version 1.0.9

Partition table scan:
  MBR: protective
  BSD: not present
  APM: not present
  GPT: present

Found valid GPT with protective MBR; using GPT.
  • Delete existing partitions:
Command (? for help): d
  • Create the EFI partition:
Command (? for help): n
Partition number (1-128, default 1): 
First sector (34-83886046, default = 2048) or {+-}size{KMGTP}: 
Last sector (2048-83886046, default = 83884031) or {+-}size{KMGTP}: +512M
Current type is 8300 (Linux filesystem)
Hex code or GUID (L to show codes, Enter = 8300): EF00
Changed type of partition to 'EFI system partition'
  • Create the /boot partition:
Command (? for help): n
Partition number (2-128, default 2): 
First sector (34-83886046, default = 1050624) or {+-}size{KMGTP}: 
Last sector (1050624-83886046, default = 83884031) or {+-}size{KMGTP}: +500M
Current type is 8300 (Linux filesystem)
Hex code or GUID (L to show codes, Enter = 8300): 
Changed type of partition to 'Linux filesystem'
  • Create the encrypted swap partition:
Command (? for help): n
Partition number (3-128, default 3): 
First sector (34-83886046, default = 2074624) or {+-}size{KMGTP}: 
Last sector (2074624-83886046, default = 83884031) or {+-}size{KMGTP}: +2G
Current type is 8300 (Linux filesystem)
Hex code or GUID (L to show codes, Enter = 8300): 8309
Changed type of partition to 'Linux LUKS'
  • Create the encrypted / partition:
Command (? for help): n
Partition number (4-128, default 4): 
First sector (34-83886046, default = 6268928) or {+-}size{KMGTP}: 
Last sector (6268928-83886046, default = 83884031) or {+-}size{KMGTP}: 
Current type is 8300 (Linux filesystem)
Hex code or GUID (L to show codes, Enter = 8300): 8309
Changed type of partition to 'Linux LUKS'
  • Check and write the partitions to the disk:
Command (? for help): p
Disk /dev/sda: 83886080 sectors, 40.0 GiB
Model: Virtual disk    
Sector size (logical/physical): 512/512 bytes
Disk identifier (GUID): 45AB06C7-DDAF-45D4-A781-B5C33DAF56D2
Partition table holds up to 128 entries
Main partition table begins at sector 2 and ends at sector 33
First usable sector is 34, last usable sector is 83886046
Partitions will be aligned on 2048-sector boundaries
Total free space is 4029 sectors (2.0 MiB)

Number  Start (sector)    End (sector)  Size       Code  Name
   1            2048         1050623   512.0 MiB   EF00  EFI system partition
   2         1050624         2074623   500.0 MiB   8300  Linux filesystem
   3         2074624         6268927   2.0 GiB     8309  Linux LUKS
   4         6268928        83884031   37.0 GiB    8309  Linux LUKS

Command (? for help): w

Final checks complete. About to write GPT data. THIS WILL OVERWRITE EXISTING
PARTITIONS!!

Do you want to proceed? (Y/N): Y
OK; writing new GUID partition table (GPT) to /dev/sda.
The operation has completed successfully.

Format the partitions

  • Format the EFI partition:
root@archiso ~ # mkfs.fat -F32 /dev/sda1
  • Format the /boot partition:
root@archiso ~ # mkfs.ext2 /dev/sda2
  • Create a LUKS partition for the swap:
Note: The complexity of the password will not be important here because we will use a random password for the swap partition afterwards.
root@archiso ~ # cryptsetup luksFormat /dev/sda3

WARNING!
========
This will overwrite data on /dev/sda3 irrevocably.

Are you sure? (Type 'yes' in capital letters): YES
Enter passphrase for /dev/sda3: STDp@$$
Verify passphrase: STDp@$$
cryptsetup luksFormat /dev/sda3  16.55s user 1.08s system 86% cpu 20.303 total
  • Decrypt the swap partition:
root@archiso ~ # cryptsetup luksOpen /dev/sda3 swap
Enter passphrase for /dev/sda3: STDp@$$
  • Check if the swap device is present:
root@archiso ~ # ls /dev/mapper
control  swap
  • Format the swap device:
root@archiso ~ # mkswap /dev/mapper/swap
  • Create a LUKS partition for /:
root@archiso ~ # cryptsetup luksFormat /dev/sda4
WARNING: Device /dev/sda4 already contains a 'dos' partition signature.

WARNING!
========
This will overwrite data on /dev/sda4 irrevocably.

Are you sure? (Type 'yes' in capital letters): YES
Enter passphrase for /dev/sda4: ComplexSTDp@$$
Verify passphrase: ComplexSTDp@$$
cryptsetup luksFormat /dev/sda4  9.72s user 0.68s system 75% cpu 13.721 total
  • Decrypt the / (root) partition:
root@archiso ~ # cryptsetup luksOpen /dev/sda4 root
Enter passphrase for /dev/sda4: ComplexSTDp@$$
  • Check if the root device is present:
root@archiso ~ # ls /dev/mapper
control  root  swap
  • Format the / (root) partition:
root@archiso ~ # mkfs.btrfs /dev/mapper/root

Mount the file system and chroot to /mnt/

Mount to /mnt

  • Mount the / (root) partition:
root@archiso ~ # mount /dev/mapper/root /mnt/
  • Enable the swap partition:
root@archiso ~ # swapon /dev/mapper/swap
  • Mount the /boot partition:
root@archiso ~ # mount --mkdir /dev/sda2 /mnt/boot
  • Mount the /efi partition:
root@archiso ~ # mount --mkdir /dev/sda1 /mnt/efi
Create Btrfs subvolumes (Optional)

If needed, Btrfs subvolumes can be created at this stage. For example, let's create a /home subvolume:

  • Create the /home subvolume:
root@archiso ~ # btrfs subvolume create /mnt/home
  • Mount the /home subvolume:
root@archiso ~ # mount /dev/mapper/root -o subvol=home /mnt/home

Install

Install essential packages

  • Install essential packages:
root@archiso ~ # pacstrap /mnt base linux linux-firmware btrfs-progs vim grub efibootmgr
  • Generate the fstab file:
root@archiso ~ # genfstab -U /mnt >> /mnt/etc/fstab
  • Edit the /mnt/etc/fstab file and replace the swap entry with this line:
/dev/mapper/swap             none            swap defaults   0 0
  • Chroot to /mnt:
root@archiso ~ # arch-chroot /mnt

Time zone

  • Identify your Time zone:
[root@archiso /]# ls /usr/share/zoneinfo/
  • Set your Time zone:
[root@archiso /]# ln -sf /usr/share/zoneinfo/Region/City /etc/localtime
  • Example:
[root@archiso /]# ln -sf /usr/share/zoneinfo/Asia/Tokyo /etc/localtime
  • Generate the /etc/adjtime file:
[root@archiso /]# hwclock --systohc

Localization

  • Edit the /etc/locale.gen file and uncomment the en_US.UTF-8 UTF-8 line, along with any other needed locales. For example:
[…]
#en_SG ISO-8859-1  
en_US.UTF-8 UTF-8
#en_US ISO-8859-1  
[…]
#fr_CH ISO-8859-1  
fr_FR.UTF-8 UTF-8
#fr_FR ISO-8859-1
  • Generate the locales:
[root@archiso /]# locale-gen
  • Create the /etc/locale.conf file and set the LANG variable accordingly:
    • For example:
[root@archiso /]# echo 'LANG=en_US.UTF-8' > /etc/locale.conf
[root@archiso /]# echo 'LANG=fr_FR.UTF-8' > /etc/locale.conf
  • Set keyboard layout:
    • For example:
[root@archiso /]# echo 'KEYMAP=fr' > /etc/vconsole.conf
[root@archiso /]# echo 'KEYMAP=us' > /etc/vconsole.conf

Network configuration

  • Set the hostname:
[root@archiso /]# echo 'stdesktop' > /etc/hostname

Initramfs

  • Edit the /etc/mkinitcpio.conf file to enable luks (to decrypt encrypted devices):
MODULES=(dm_mod ext2 btrfs ext4 xfs)
[…]
#add lvm2, mdadm if needed
HOOKS=(base udev autodetect modconf block keyboard keymap encrypt filesystems usr fsck shutdown)
  • Generate a new initramfs:
[root@archiso /]# mkinitcpio -P
  • Add the swap entry in the /etc/crypttab file to set a random key for the encrypted swap partition:
[root@archiso /]# echo 'swap    /dev/sda3       /dev/urandom            swap,cipher=aes-cbc-essiv:sha256,size=256' >> /etc/crypttab

Boot Loader

  • Edit the /etc/default/grub file. Here, you can:
    • Use amdgpu.audio=0 to disable HDMI/DP Audio if it is not needed. (See complete module parameters here).
    • Specify your crypted root device as /dev/sda4:root.
    • Remove the quiet option if you prefer a verbose boot for debugging purposes.
GRUB_CMDLINE_LINUX_DEFAULT="loglevel=3 amdgpu.audio=0"
#we can use /dev/sda4 UUID like that: cryptdevice=UUID=XXXX-XXXX-XXXX:root if we prefer
GRUB_CMDLINE_LINUX="cryptdevice=/dev/sda4:root"
  • Install the GRUB EFI application to /efi/ (sda1):
[root@archiso /]# grub-install --target=x86_64-efi --efi-directory=/efi/ --bootloader-id=GRUB

Installing for x86_64-efi platform.
Installation finished. No error reported.
  • Generate the main configuration file grub.cfg:
[root@archiso /]# grub-mkconfig -o /boot/grub/grub.cfg
Generating grub configuration file ...
Found linux image: /boot/vmlinuz-linux
Found initrd image: /boot/initramfs-linux.img
Found fallback initrd image(s) in /boot:  initramfs-linux-fallback.img
Warning: os-prober will not be executed to detect other bootable partitions.
Systems on them will not be added to the GRUB boot configuration.
Check GRUB_DISABLE_OS_PROBER documentation entry.
Adding boot menu entry for UEFI Firmware Settings ...
done

Reboot

  • Set the root password:
[root@archiso /]# passwd
  • Remove the boot media and reboot the system:
[root@archiso /]# exit
root@archiso ~ # reboot

Post-Install

Add a user

  • Create a new user:
[root@stdesktop ~]# useradd -m newuser
  • Set a password for the new user:
[root@stdesktop ~]# passwd newuser

Network

The network can be configured manually, with a static IP, or via DHCP.

  • The first thing to do is identify your network interface:
[root@stdesktop ~]# ip link show
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN mode DEFAULT group default qlen 1000
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
2: ens192: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc fq_codel state UP mode DEFAULT group default qlen 1000
    link/ether 24:1f:fe:d3:bc:2a brd ff:ff:ff:ff:ff:ff

Manually with ip commands

Note: This configuration will be reset after each reboot.

  • Enable the network interface:
[root@stdesktop ~]# ip link set ens192 up
  • Set the IP address:
[root@stdesktop ~]# ip addr add 192.168.1.10/24 dev ens192
  • Set the gateway:
[root@stdesktop ~]# ip route add default via 192.168.1.254
  • Set the DNS:
[root@stdesktop ~]# echo 'nameserver 80.67.169.12' > /etc/resolv.conf

Statically with systemd-networkd

  • Create a /etc/systemd/network/ens192.network file:
[Match]
Name=ens192

[Network]
Address=192.168.1.10/24
Gateway=192.168.1.254
DNS=192.168.1.254
  • Enable and start the systemd-networkd service:
[root@stdesktop ~]# systemctl enable systemd-networkd.service && systemctl start systemd-networkd.service

DHCP with systemd-networkd

  • Create a /etc/systemd/network/ens192.network file:
[Match]
Name=ens192

[Network]
DHCP=yes
  • Enable and start the systemd-networkd service:
[root@stdesktop ~]# systemctl enable systemd-networkd.service && systemctl start systemd-networkd.service

Desktop environment

  • Install GNOME (simply press enter to each question):
[root@stdesktop ~]# pacman -S gnome
  • If you are not using the qwerty keyboard layout, set the gdm keyboard layout according to your language:
[root@stdesktop ~]# localectl set-x11-keymap fr
  • Enable and start the gdm service:
[root@stdesktop ~]# systemctl enable gdm
[root@stdesktop ~]# systemctl start gdm
GNOME Display Manager (GDM) login screen on ArchLinux, showing a user named 'newuser' with a dark theme.

Misc

Hardware acceleration for the AMD FirePro W5000

If you have an AMD FirePro W5000, you may want to enable hardware video acceleration.

  • Install the libva-mesa-driver and mesa-vdpau packages:
[root@stdesktop ~]# pacman -S libva-mesa-driver mesa-vdpau
  • Install the vainfo, vdpauinfo, and radeontop tools:
[root@stdesktop ~]# pacman -S libva-utils vdpauinfo radeontop
  • Create the /etc/modprobe.d/amdgpu.conf file:
options amdgpu si_support=1
  • Create the /etc/modprobe.d/radeon.conf file:
options radeon si_support=0
options radeon cik_support=0
  • Edit the /etc/mkinitcpio.conf file:
MODULES=(dm_mod ext2 btrfs ext4 xfs amdgpu)
[...]
HOOKS=(base udev autodetect modconf block keyboard keymap encrypt lvm2 filesystems usr fsck shutdown)
  • Generate a new initramfs:
[root@archiso /]# mkinitcpio -P
  • Reboot and check if the amdgpu driver is loaded:
[newuser@stdesktop ~]$ lspci -k | grep -A 3 -E "(VGA|3D)"
01:00.0 VGA compatible controller: Advanced Micro Devices, Inc. [AMD/ATI] Pitcairn LE GL [FirePro W5000]
	Subsystem: Advanced Micro Devices, Inc. [AMD/ATI] Device 0b06
	Kernel driver in use: amdgpu
	Kernel modules: radeon, amdgpu
  • Add the VDPAU_DRIVER environment variable:
[newuser@stdesktop ~]$ echo 'export VDPAU_DRIVER=radeonsi' >> ~/.bashrc
  • Verify VA-API:
[newuser@stdesktop ~]$ vainfo
vainfo: VA-API version: 1.15 (libva 2.15.0)
vainfo: Driver version: Mesa Gallium driver 22.1.7 for ATI FirePro V(FireGL V) Graphics Adapter (pitcairn, LLVM 14.0.6, DRM 3.47, 5.19.9-arch1-1)
vainfo: Supported profile and entrypoints
      VAProfileMPEG2Simple            : VAEntrypointVLD
      VAProfileMPEG2Main              : VAEntrypointVLD
      VAProfileVC1Simple              : VAEntrypointVLD
      VAProfileVC1Main                : VAEntrypointVLD
      VAProfileVC1Advanced            : VAEntrypointVLD
      VAProfileH264ConstrainedBaseline: VAEntrypointVLD
      VAProfileH264Main               : VAEntrypointVLD
      VAProfileH264High               : VAEntrypointVLD
      VAProfileNone                   : VAEntrypointVideoProc
  • Verify VDPAU:
[newuser@stdesktop ~]$ vdpauinfo
display: :0   screen: 0
API version: 1
Information string: G3DVL VDPAU Driver Shared Library version 1.0

Video surface:

name   width height types
-------------------------------------------
420    16384 16384  NV12 YV12 
422    16384 16384  UYVY YUYV 
444    16384 16384  Y8U8V8A8 V8U8Y8A8 
420_16 16384 16384  
422_16 16384 16384  
444_16 16384 16384  

Decoder capabilities:
[…]

Open applications at login

When logging in, you can have your most frequently used applications open automatically.

  • Create the ~/.config/autostart/ folder:
[newuser@stdesktop ~]$ mkdir ~/.config/autostart
  • Create the ~/.config/autostart/apps.desktop file:
[Desktop Entry]
Name=AutostartScript
GenericName=Gnome Auto Start Script
Comment=Script which launch applications at startup
Exec=/home/newuser/.config/autostart.sh
Terminal=false
Type=Application
X-GNOME-Autostart-enabled=true
  • Create the ~/.config/autostart.sh file:
#Open terminal
#gnome-terminal&
kgx&
#Open Firefox
firefox &
#Open Explorer
nautilus&
  • Add execution rights:
[newuser@stdesktop ~]$ chmod +x ~/.config/autostart.sh

Crontab

I usually use cron as a task scheduler. While there are implementations of cron in ArchLinux, none of them are pre-installed. By default, the base system uses systemd/Timers. Here, we will see how to create a systemd/Timers task.

  • Create a /etc/systemd/system/mytask.service file that will run a program (in this example, we copy /home/newuser/a_file to a /backup location using the date command substitution):
[Unit]
Description=Copy a file
Wants=mytask.timer

[Service]
Type=oneshot
User=newuser
#Normaly $(/usr/bin/date +%Y%m%d) but $ and % need to be escaped
ExecStart=/bin/bash -c  '/usr/bin/cp /home/newuser/a_file /backup/a_file.$$(/usr/bin/date +%%Y%%m%%d)'

[Install]
WantedBy=multi-user.target
  • Create the associated /etc/systemd/system/mytask.timer file where we will set the trigger:
[Unit]
Description=Copy a file trigger
Requires=mytask.service

[Timer]
Unit=mytask.service
#It will run each day at 19h00
OnCalendar=*-*-* 19:00:00

[Install]
WantedBy=timers.target
  • Enable the timer and reload the systemd configuration:
[root@stdesktop ~]# systemctl enable mytask.timer && systemctl daemon-reload
  • Check the status of mytask.timer:
[root@stdesktop ~]# systemctl status mytask.timer
● mytask.timer - Copy a file
     Loaded: loaded (/etc/systemd/system/mytask.timer; enabled; preset: disabled)
     Active: active (waiting) since Sun 2022-09-25 16:28:31 CEST; 26min ago
      Until: Sun 2022-09-25 16:28:31 CEST; 26min ago
    Trigger: Sun 2022-09-25 19:00:00 CEST; 2h 4min left
   Triggers: ● mytask.service

sept. 25 16:28:31 stddesktop.local systemd[1]: Started Copy a file.

References