From ArchWiki

Arch Linux installation.


Put the firmware in "Setup Mode"

This step depends on BIOS implementation. First you need to enable UEFI boot only (no legacy boot). Then change Secure Boot option to Setup Mode. Usually it is under Secure Boot section, the exact name may be different, sometimes called Custom Mode. To check the Secure Boot is set to the Setup Mode, boot into the live iso. Then run

# bootctl status | grep "Secure Boot"
Secure Boot: disabled (setup)

It should have (setup).

UEFI Boot manager efibootmgr(8)

Display current boot settings

# efibootmgr
BootCurrent: 0004
BootNext: 0003
BootOrder: 0004,0000,0001,0002,0003
Timeout: 30 seconds
Boot0000* Diskette Drive(device:0)
Boot0001* CD-ROM Drive(device:FF)
Boot0002* Hard Drive(Device:80)/HD(Part1,Sig00112233)
Boot0003* PXE Boot: MAC(00D0B7C15D91)
Boot0004* Linux

Delete unused boot options. For example

# efibootmgr -b 0004 -B

will delete boot entry 0004.

Set the console keyboard layout

# loadkeys us

Check UEFI mode

If booted as UFEI mode

# ls /sys/firmware/efi/efivars

should should print the directory without error.

Check internet connection

# ping

Recommend using wired connection during installation. Use a USB to Ethernet dongle if necessary.

Check system clock

# timedatectl status

Partition the disks

Use fdisk create three partitions:

Warning: Parted only aligns partition start, but not the size/end. This is not enough for dm-crypt/LUKS, see Parted#Alignment and Advanced Format#Partition alignment.

Setup encrypted root partition

Encrypt the device with LUKS2 mode

# cryptsetup --type luks2 --verify-passphrase --sector-size 4096 --verbose luksFormat /dev/sdX2

SSD usually report their sector size as 512 bytes, even though they use larger sector size. So add --sector-size 4096 force create a LUKS2 container with 4K sector size. If the sector size is wrong cryptsetup will abort with an error. To re-encrypt with correct sector size see Advanced Format#dm-crypt.

Unlock the LUKS2 container

# cryptsetup open /dev/sdX2 cryptroot

It will map the encrypted container to new device /dev/mapper/cryptroot.

Format the partitions

  • EFI partition
    # mkfs.fat -F 32 /dev/sdX1
  • Root partition
    # mkfs.btrfs /dev/mapper/cryptroot
  • Swap partition
    # mkswap /dev/sdX3
    swap encryption will be configured after arch-bootstrap.

Create Btrfs subvolumes

# mount /dev/mapper/cryptroot /mnt

# btrfs subvolume create /mnt/@
# btrfs subvolume create /mnt/@home
# btrfs subvolume create /mnt/@snapshots
# btrfs subvolume create /mnt/@var_log
# btrfs subvolume create /mnt/@pacman_pkgs

# mkdir /mnt/@/home
# mkdir /mnt/@/.snapshots
# mkdir /mnt/@/efi
# mkdir -p /mnt/@/var/log
# mkdir -p /mnt/@/var/cache/pacman/pkg

# umount -R /mnt

Following Snapper#Suggested filesystem layout.

Mount all file systems

# mount -o ssd,noatime,compress=zstd:1,space_cache=v2,autodefrag,subvol=@ /dev/mapper/cryptroot /mnt
# mount -o ssd,noatime,compress=zstd:1,space_cache=v2,autodefrag,subvol=@home,nodev,nosuid /dev/mapper/cryptroot /mnt/home
# mount -o ssd,noatime,compress=zstd:1,space_cache=v2,autodefrag,subvol=@snapshots,nodev,nosuid,noexec /dev/mapper/cryptroot /mnt/.snapshots
# mount -o ssd,noatime,compress=zstd:1,space_cache=v2,autodefrag,subvol=@var_log,nodev,nosuid,noexec /dev/mapper/cryptroot /mnt/var/log
# mount -o ssd,noatime,compress=zstd:1,space_cache=v2,autodefrag,subvol=@pacman_pkgs,nodev,nosuid,noexec /dev/mapper/cryptroot /mnt/var/cache/pacman/pkg

# mount /dev/sdX1 /mnt/efi

# swapon /dev/sdX3

Check btrfs(5) § MOUNT OPTIONS and Security#Mount options for the explanations. Here I assume the root partition is on a SSD. If you are still using spinning rust for your boot drive, change mount option ssd to nossd.


Add SELinux repository (optional)

Warning: Unofficial repository use at your own risk.

Enable Unofficial user repositories#selinux by adding these lines to the end of /etc/pacman.conf

Server =
SigLevel = PackageOptional

Install essential packages

  • Without SELinux:
    # pacstrap -K /mnt base base-devel linux linux-firmware man-db vim dosfstools e2fsprogs btrfs-progs cpu_manufacturer-ucode
  • With SELinux: replace base and base-devel with base-selinux and base-devel-selinux. Also add archlinux-keyring to the list:
    # pacstrap -K /mnt base-selinux base-devel-selinux linux linux-firmware man-db vim dosfstools e2fsprogs btrfs-progs archlinux-keyring cpu_manufacturer-ucode
The base-selinuxAUR depends selinux-refpolicy-archAUR, no need to install extra policy.

Replace cpu_manufacturer-ucode with amd-ucode or intel-ucode depends on the CPU manufacturer.

Configure the system


Generate an fstab

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

Remove all subvolid= options in fstab

# <file system>    <dir>   <type>    <options>    <dump> <pass>
# /dev/mapper/cryptroot
UUID=xxxx  /  btrfs  rw,noatime,compress=zstd:1,ssd,space_cache=v2,autodefrag,subvolid=256,subvol=/@  0 0

# /dev/sdX1
UUID=xxxx  /efi  vfat ...

# /dev/mapper/cryptroot
UUID=xxxx  /home  btrfs  rw,noatime,compress=zstd:1,ssd,space_cache=v2,autodefrag,subvolid=257,subvol=/@home  0 0


# /dev/sdX3
UUID=xxxx  none  swap  defaults  0 0

You can run this command remove all subvolid options

# sed -i 's/subvolid=[0-9]*,//g' /mnt/etc/fstab

This is helpful because you do not need to edit fstab file after restoring root partition to its previous snapshot.


# arch-chroot /mnt
# export PS1="(chroot) ${PS1}"

Time zone

(chroot) # ln -sf /usr/share/zoneinfo/Region/City /etc/localtime
(chroot) # hwclock --systohc


Uncomment en_US.UTF-8 UTF-8 in /etc/locale.gen, then

(chroot) # locale-gen

Create /etc/locale.conf and /etc/vconsole.conf

(chroot) # echo "LANG=en_US.UTF-8" > /etc/locale.conf
(chroot) # echo "KEYMAP=us" > /etc/vconsole.conf

Network configuration


(chroot) # echo archlinux > /etc/hostname

Choose only one network manager.

  • systemd-networkd is my preferred choice for devices that only need wired connection. iwd works well for WPA-Personal WiFi, but not easy to configure for the WPA-Enterprise WiFi.
  • NetworkManager with wpa_supplicant for devices need wireless connection.


Create configuration file



Enable systemd-resolved.service and systemd-networkd.service.


Install networkmanager and wpa_supplicant. Then enable systemd-resolved.service, NetworkManager.service and wpa_supplicant.service.

Disk encryption

Configure mkinitcpio

A configuration with systemd-based initramfs using sd-encrypt would looks similar to

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

Create /etc/crypttab.initramfs

cryptroot  UUID=ROOT_UUID  -  password-echo=no,x-systemd.device-timeout=0,timeout=0,no-read-workqueue,no-write-workqueue,discard

You can get ROOT_UUID with command lsblk -dno UUID /dev/sdX2. The no-read-workqueue,no-write-workqueue,discard options at the end is for SSD, see dm-crypt/Specialties#Disable workqueue for increased solid state drive (SSD) performance and dm-crypt/Specialties#Discard/TRIM support for solid state drives (SSD).

Swap encryption

To use UUID instead of /dev/sdX3 in the /etc/crypttab, we need to create a small 1MiB sized filesystem at /dev/sdX3, see dm-crypt/Swap encryption#UUID and LABEL. First deactivate swap partition

(chroot) # swapoff /dev/sdX3

Then create a 1MiB ext2 filesystem with label cryptswap

(chroot) # mkfs.ext2 -F -F -L cryptswap /dev/sdX3 1M

Add /etc/crypttab entry

# <name>   <device>         <password>   <options>
cryptswap  UUID=SWAP_UUID  /dev/urandom  swap,offset=2048

Get SWAP_UUID with command lsblk -dno UUID /dev/sdX3. The option offset is the start offset in 512-byte sectors. So 2048 of 512 bytes is 1MiB.

Edit the swap entry in /etc/fstab, change UUID=xxxx to /dev/mapper/cryptswap

# <filesystem>    <dir>  <type>  <options>  <dump>  <pass>
/dev/mapper/swap  none   swap    defaults   0       0

Unified kernel image

Kernel command line

Create /etc/kernel/cmdline and /etc/kernel/cmdline_fallback file, which will contains kernel parameters for the unified kernel image. Fallback images would use cmdline_fallback as kernel parameters. You could have different kernel parameters for fallback images.

root=/dev/mapper/cryptroot rootfstype=btrfs rootflags=subvol=/@ rw modprobe.blacklist=pcspkr
root=/dev/mapper/cryptroot rootfstype=btrfs rootflags=subvol=/@ rw modprobe.blacklist=pcspkr

modprobe.blacklist=pcspkr disable the PC speaker (or beeper) globally, see PC speaker#Disabling the PC speaker.

If SELinux enabled also add lsm=landlock,lockdown,yama,integrity,selinux,bpf to the end.

Modify .preset file

Edit all .preset files for the kernels you installed, eg. linux.preset, linux-zen.preset, linux-lts.preset etc.

  • Add line ALL_microcode=(/boot/*-ucode.img)
  • Add default_uki= and fallback_uki=
  • Optionally, comment out default_image= and fallback_imag=
  • Optionally, add Arch splash screen by appending --splash to default_options=
# mkinitcpio preset file for the 'linux' package


PRESETS=('default' 'fallback')

default_options="--splash /usr/share/systemd/bootctl/splash-arch.bmp"

fallback_options="-S autodetect --cmdline /etc/kernel/cmdline_fallback"

If installed linux-zen kernel then linux-zen.preset would looks like

# mkinitcpio preset file for the 'linux-zen' package


PRESETS=('default' 'fallback')

default_options="--splash /usr/share/systemd/bootctl/splash-arch.bmp"

fallback_options="-S autodetect --cmdline /etc/kernel/cmdline_fallback"

This will generate unified kernel image Archlinux-linux.efi.

Finally regenerate the initramfs

(chroot) # mkinitcpio -P

and remove any leftover initramfs-*.img from /boot or /efi.

Secure boot

Install the sbctl package.

Create keys

(chroot) # sbctl create-keys

Enroll keys

Warning: Some plug-in cards have firmware that's signed by Microsoft's keys. Such a card will not work (at least, not from the firmware) if you enable Secure Boot without the matching public key. (The card should still work once you've booted an OS. The biggest concern is for cards that must work in a pre-boot environment, such as a video card or for PXE-booting a network card.) You can add Microsoft's keys back to the environment to make such cards work, but this eliminates the advantages of not having those keys, if that's a significant factor for you.[1] Without Microsoft 3rd Party UEFI CA certificate these devices would not work before OS booted. Which could be a problem if your CPU does not have an integrated graphics card. If the discrete GPU does not work before OS is booted, you won't be able to access BIOS. You can skip this section, if you do not have such devices.

If you don't want to enroll Microsoft key run

(chroot) # sbctl enroll-keys

If you want to enroll Microsoft key run

(chroot) # sbctl enroll-keys --microsoft
  • If sbctl says You need to chattr -i files in efivarfs, first run
    (chroot) # chattr -i /sys/firmware/efi/efivars/{PK,KEK,db}*
    then enroll key again.
  • If running libvirt virtual machine, you may add --yes-this-might-brick-my-machine to force enroll keys.

Sign unified kernel image

Sign all unified kernel images

(chroot) # sbctl sign --save /efi/EFI/Linux/ArchLinux-linux
(chroot) # sbctl sign --save /efi/EFI/Linux/ArchLinux-linux-fallback.efi

If installed linux-zen, you also need to sign those images

(chroot) # sbctl sign --save /efi/EFI/Linux/ArchLinux-linux-zen
(chroot) # sbctl sign --save /efi/EFI/Linux/ArchLinux-linux-zen-fallback.efi

This will replace old unsigned images with new signed ones.

Note: If you install extra kernels after this installation, do not forget to modify its .preset file regenerate the initramfs. Then sign the unified kernel images and add UEFI boot entries. Finally run sbctl sign-all since you regenerated initramfs.

UEFI boot entries

Install efibootmgr.

For linux kernel

(chroot) # efibootmgr --create --disk /dev/sdX --part 1 --label "ArchLinux-linux" --loader "EFI\\Linux\\ArchLinux-linux.efi"
(chroot) # efibootmgr --create --disk /dev/sdX --part 1 --label "ArchLinux-linux-fallback" --loader "EFI\\Linux\\ArchLinux-linux-fallback.efi"

--disk is the disk containing boot loader do not include part number, it is /dev/sdX not /dev/sdX1. The --part specify the partition number. If the EFI partition is /dev/sdX2 then it is --disk /dev/sdX --part 2.

If you install linux-zen kernel also need to add them to boot entry, for example

(chroot) # efibootmgr --create --disk /dev/sdX --part 1 --label "ArchLinux-linux-zen" --loader "EFI\\Linux\\ArchLinux-linux-zen.efi"
(chroot) # efibootmgr --create --disk /dev/sdX --part 1 --label "ArchLinux-linux-zen-fallback" --loader "EFI\\Linux\\ArchLinux-linux-zen-fallback.efi"

Change the boot order, for example I want first boot entry 0003 then 0004, then 0005, run

(chroot) # efibootmgr --bootorder 0003,0004,0005

Set root password

(chroot) # passwd

Reboot into BIOS

(chroot) # exit
# umount -R /mnt
# systemctl reboot --firmware-setup
  • Now you can enable Secure Boot (also called User Mode) in the BIOS. Some motherboard manufacture will automatically change to User Mode if you enrolled your own key.
  • Then set a BIOS password.
  • Now finger crossed and boot into your new system.


SELinux#Post-installation steps

Label your filesystem

# restorecon -r /
# systemctl reboot

Check SELinux Status:

# sestatus

It should be permissive mode. To temporary switch to enforcing mode

# echo 1 > /sys/fs/selinux/enforce

Or edit /etc/selinux/config to switch permanently.

Enable restorecond.service to maintain correct context.


From Lennart Poettering's blog:

It (systemd-homed) also allows us to correct another major issue with traditional Linux systems: the way how data encryption works during system suspend. Traditionally on Linux the disk encryption credentials (e.g. LUKS passphrase) is kept in memory also when the system is suspended. This is a bad choice for security, since many (most?) of us probably never turn off their laptop but suspend it instead. But if the decryption key is always present in unencrypted form during the suspended time, then it could potentially be read from there by a sufficiently equipped attacker.

In other words, my data is still safe even if I leave my laptop suspended in a hotel. In my humble opinion, systemd-homed does not provide much benefits for server or single user desktop use cases. Also it needs extra configuration if running SSH server, see systemd-homed#SSH remote unlocking.

If you want to add a user using systemd-homed keep reading this section, otherwise follow Users and groups#User management.

Start and enable systemd-homed.service.

# homectl create tux --uid=1000 --member-of=wheel --shell=/bin/bash --storage=luks --fs-type=btrfs

Will add user tux with UID=1000 and a member of wheel group. Its home directory is a encrypted LUKS volume, and the filesystem is btrfs.

Disable root login

# passwd -d root
# passwd -l root

Time servers


Then start and enable systemd-timesyncd.service

Pacman#Enabling parallel downloads

Uncomment ParallelDownloads in /etc/pacman.conf.


Install the reflector package.

--save /etc/pacman.d/mirrorlist
--protocol https
--country us
--latest 5
--sort age

Enable reflector.service and reflector.timer.

Pacman#Cleaning the package cache

Install the pacman-contrib package. Enable paccache.timer.

Solid state drive#Periodic TRIM

Enable fstrim.timer.

Optimize AUR building

Remove any -march and -mtune CFLAGS, then add -march=native in /etc/makepkg.conf.

CFLAGS="-march=native -O2 -pipe ..."

Add -C target-cpu=native to RUSTFLAGS:

RUSTFLAGS="-C opt-level=2 -C target-cpu=native"

Enable parallel compilation



Install the firewalld package. Start and enable firewalld.service.


I want to use systemd manage snapshots instead of cron. Uncomment NoExtract in /etc/pacman.conf

NoExtract = etc/cron.daily/snapper etc/cron.hourly/snapper

Install the snapper package.

Follow Snapper#Configuration of snapper and mount point suggestion create configuration for /. Unmount and remove existing /.snapshots

# umount /.snapshots
# rm -r /.snapshots

Create a new config for / named root.

# snapper -c root create-config /

Delete the subvolume created by snapper, and recreate the directory

# btrfs subvolume delete /.snapshots
# mkdir /.snapshots

Remount @snapshots to /.snapshots and set right permission.

# mount -a
# chmod -R 750 /.snapshots

Create new config for /home

# snapper -c home create-config /home
Note: If using systemd-homed create a config for /home/tux instead. /home only contains encrypted volumes.

Edit /etc/snapper/configs/root and /etc/snapper/configs/home




Enable snapper-timeline.timer and snapper-cleanup.timer.

Install the snap-pac and rsync packages to create snapshot on pacman transactions. Create this pacman hook to backup /efi directory.

Operation = Upgrade
Operation = Install
Operation = Remove
Type = Package
Target = *

Depends = rsync
Description = Backing up /efi...
When = PostTransaction
Exec = /usr/bin/rsync --archive --delete /efi/ /.efibackup

Pacman post transaction backup:

New unified kernel images are generated in /efi/EFI/Linux/. Then zz-sbctl.hook from the sbctl package will sign and replace these unsigned kernel images. Then this zz-signed_uki_backup.hook will copy /efi directory to /.efibackup. Finally, zz-snap-pac-post.hook from the snap-pac package will snapshot /.

Pacman pre transaction backup:

05-snap-pac-pre.hook from snap-pac snapshot / before pacman transaction. /.efibackup contains old signed kernel images.
Warning: This setup is not intended to be used with snapper rollback, but is intended to alleviate the inherent problems of #Restoring / to its previous snapshot. See this this forum thread. To rollback you need to temporary disable secure boot and boot into Arch ISO, then manually restore snapshot.

To restore /, see snapper#Restoring / to its previous snapshot.

Restore dotfiles from a Git bare repository

Note: To setup a Git bare repository for the first time, see this

Install the git package.

Clone your dotfiles repository

$ git clone --bare dotfiles-repo-url $HOME/.dotfiles
$ alias dotfiles='/usr/bin/git --git-dir=$HOME/.dotfiles/ --work-tree=$HOME'

Checkout the repository

$ dotfiles checkout

It may show an error of conflicting files that already exist in your home directory would be overwritten. Since this is fresh installed system, we simply delete them. Or run this command then checkout again

$ dotfiles checkout 2>&1 | egrep "\s+\." | awk {'print $1'} | xargs -I{} rm {}
$ dotfiles checkout

If using GitHub change the remote url to use SSH key

[remote "origin"]
    url =

Install the openssh package or openssh-selinuxAUR if using SELinux. Generate new SSH keys#Ed25519 pairs

$ ssh-keygen -t ed25519

After installed Graphical user interface upload your new SSH key to GitHub. Now you can push to your Git repository with

$ dotfiles push


Install the zsh, zsh-completions, zsh-syntax-highlighting, zsh-autosuggestions and grml-zsh-config packages, which provides the same setup as the Arch ISO release. To have Fish-like syntax highlighting and autosuggestions, add these lines to your .zshrc:

source /usr/share/zsh/plugins/zsh-syntax-highlighting/zsh-syntax-highlighting.zsh
source /usr/share/zsh/plugins/zsh-autosuggestions/zsh-autosuggestions.zsh

To make Zsh your default shell

$ chsh -s /usr/bin/zsh

or if you are using systemd-homed

# homectl update --shell=/usr/bin/zsh tux

Graphical user interface

Laptop power management