I’ve been wanting to install Arch on my primary workstation for a while though I was pretty overwhelmed with figuring out what sort of configuration I wanted. I now have something that I like and wanted to outline what I did. This is mostly for my own reference and documentation, though please feel free to use this and adapt it to your own needs!

This guide will outline how to setup a clean install of Arch linux with the following:

  • Secure Boot.
  • LUKS + BTRFS with subvolumes.
  • AppArmor.
  • systemd-boot.
  • TPM 2.0 enrollment for LUKS.
  • GNOME 42 w/ Wayland and NVIDIA proprietary driver.

Note

Please do your own research and check the latest Arch Install Guide for guidance. Sorry if this breaks your system uwu

Base Install

The Arch Install Guide is honestly the best way to get started and is a great reference if you get stuck along the way.

Before you get started, make sure that you have UEFI Mode enabled and Secure Boot disabled on your system. We will enable Secure Boot later in this guide.

WiFi and Syncing System Clock

You will probably want to connect to WiFi, unless you are directly plugged in or are setting up an air-gapped system.

iwctl is probably the easiest way to do this.

1
2
3
$ iwctl
[iwd]# device list
[iwd]# station <device> connect <SSID>

You can also do this as a one-liner, with the AP passphrase: iwctl --passphrase <passphrase> station <device> connect <SSID>

Once you verify that you are connected to the internet, go ahead and sync your system clock using NTP: timedatectl set-ntp true

Partitions

Warning

Make sure you are using the correct drive! Always verify so you don’t accidently nuke your data.

We will want to verify which hard drive we are going to install Arch to. The easiest way to do this is to use lsblk or fdisk -l. Personally, lsblk is prettier and I much prefer it :)

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
$ lsblk
NAME                                MAJ:MIN RM   SIZE RO TYPE  MOUNTPOINTS
sda                                   8:0    0   3.6T  0 disk  
└─sda1                                8:1    0   3.6T  0 part  
sdb                                   8:16   0   4.5T  0 disk  
└─sdb1                                8:17   0   4.5T  0 part  
sdc                                   8:18   0   8.5T  0 disk  
└─sdc1                                8:19   0   8.5T  0 part  

nvme1n1                             259:0    0 931.5G  0 disk  
├─nvme1n1p1                         259:4    0   100M  0 part  
├─nvme1n1p2                         259:5    0    16M  0 part  
├─nvme1n1p3                         259:6    0 930.8G  0 part  
└─nvme1n1p4                         259:7    0   593M  0 part

nvme0n1                             259:1    0 931.5G  0 disk  
├─nvme0n1p1                         259:4    0   100M  0 part  
├─nvme0n1p2                         259:5    0    16M  0 part  
├─nvme0n1p3                         259:6    0 930.8G  0 part  
└─nvme0n1p4                         259:7    0   593M  0 part  

My system has multiple hard drives. In this case, I am going to install Arch on one of the 1TB NVME SSDs, nvme1n1.

Now to create the system partitions, let’s start with the EFI boot partition:

1
2
3
4
5
6
7
$ gdisk /dev/nvme1n1
Command (? for help): o
Command (? for help): n
Default partition number.
Default first sector.
Last sector: +550M
Partition type: EF00

For the root partition, accept all of the default values:

1
2
3
4
5
Command (? for help): n
Default partition number.
Default first sector.
Default last sector.
Default partition type.

With our new partitions made, it is time to write the changes!

1
Command (? for help): w

LUKS

Tip

Consider your threat model when creating your passphrase.

Our root file system will be encrypted, so we will need a strong passphrase. Depending on your threat model, you may want to be careful about selecting a secure passphrase; also be mindful when it comes to storage of this passphrase. No sticky notes!

When creating the LUKS volume, make sure you are selecting your root partition, and not the EFI boot partition. In my case, the second partition (the root partition) is nvme1n1p2.

1
$ cryptsetup luksFormat /dev/nvme1n1p2

Once that finishes, you are going to want to open the newly created LUKS volume. You will need to name the volume when you open it, I typically just call it “luks”, though you can pick whatever you want. Just make sure you stay consistent!

1
$ cryptsetup open /dev/nvme1n1p2 luks

Format EFI and Root Partitions

Simple and easy, format the EFI boot partition with FAT32:

$ mkfs.vfat -F32 /dev/nvme1n1p1

And now the root partition, with BTRFS:

$ mkfs.btrfs /dev/mapper/luks

Create and Mount BTRFS Subvolumes

In order to take advantage of BTRFS snapshots, we will need to make subvolumes for root and home. Root being the system files that live in / and home being our user data in /home/.

1
2
3
4
$ mount /dev/mapper/luks /mnt
$ btrfs sub create /mnt/@
$ btrfs sub create /mnt/@home
$ umount /mnt

Now we are going to mount the root subvolume, make mount directories for the home subvolume and the EFI boot partition, then mount the boot partition:

1
2
3
4
$ mount -o noatime,nodiratime,compress=zstd:1,space_cache=v2,ssd,subvol=@ /dev/mapper/luks /mnt
$ mkdir -p /mnt/{boot,home}
$ mount -o noatime,nodiratime,compress=zstd:1,space_cache=v2,ssd,subvol=@home /dev/mapper/luks /mnt/home
$ mount /dev/nvme1n1p1 /mnt/boot

If you are wanting to use space cache (space_cache=v1) instead of free space tree (space_cache=v2) , check this out for more info.

Install System Packages

With the prep work out of the way, it is time to install our base system packages so we can chroot into the system :3

Info

If you are using an AMD CPU, you will want amd-ucode and not intel-ucode. Also, you may want to use linux instead of linux-zen for your kernel.

1
$ pacstrap /mnt linux-zen linux-zen-headers linux-firmware base base-devel btrfs-progs intel-ucode efibootmgr networkmanager dialog wpa_supplicant bluez bluez-utils mtools dosfstools git xdg-utils xdg-user-dirs alsa-utils pipewire pipewire-alsa pipewire-pulse apparmor sbctl vim zsh 

Feel free to change this list to fit your needs. You can always install more packages later. Also, feel free to replace vim with your editor of choice :)

After the pacstrap command finishes, you will want to generate your /etc/fstab file. This handy command will generate this file based on your current mount points. Cool, right?

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

Now for the exciting part, let’s chroot into our new linux install so we can do some additional configuration!

Chroot and System Configuration

To chroot into the new linux install you will run the following command:

1
$ arch-chroot /mnt/

You will now be presented with a root user prompt.

Go ahead and enable the following services, so we don’t forget!

1
$ systemctl enable NetworkManager apparmor bluetooth

User Accounts

First things first, we need to set a root password. Make sure you pick something secure. Again, consider your threat model!

1
$ passwd

We will also want to create our user account. I am adding the user to the wheel group, as in a moment we will be granting this user group access to use the sudo command for privilege escalation. Also, when setting the password, again I say, consider your threat model :3

1
2
$ useradd -mG wheel <username>
$ passed <username>

To add the wheel group to the suderos file we will want uncomment the following line from the default config (or add it if it isn’t there):

1
%wheel ALL=(ALL) ALL

This will allow members of the wheel group to execute any command as root.

System Hostname and Hosts Configuration

Your system’s hostname can be whatever you want, all you need to do is put it in the /etc/hostname file.

1
$ echo <hostname> > /etc/hostname

We will also want to define this hostname in our /etc/hosts/ file, as follows:

1
2
3
127.0.0.1 localhost
::1 localhost
127.0.1.1 <hostname>.localdomain <hostname>

Locale Generation and Timezone

Another important thing we need to do is define our language locale. You will want to open the /etc/locale.gen file in your editor of choice and uncomment the lines that are relevant to your language. For me, I went with U.S English, with UTF-8 encoding.

1
en_US.UTF-8 UTF-8

Note

You may need to also select your locale in GNOME’s settings later.

Now you will need to set the locale and generate:

1
2
$ echo en_US.UTF-8 UTF-8 > /etc/locale.conf
$ locale-gen

To set the timezone, you will need to figure out the correct timezone string based on your region. An easy way to do that is to use grep:

1
$ timedatectl list-timezones | grep <region>

For me, I picked America/Los_Angeles. Now to set the timezone you picked, and sync it to your system clock:

1
2
$ ln -sf /usr/share/zoneinfo/America/Los_Angeles /etc/localtime
$ hwclock --systohc

You can run date to verify the current system time.

initramfs Configuration and Generation

The initramfs configuration is super important (not to mention super neat). I would recommend that you take some time to familiarize yourself with mkinitcpio, which is the tool used to create the initial ramdisk (initramfs).

To start, we will need to modify /etc/mkinitcpio.conf to make changes to the MODULES and HOOKS sections. Go ahead and open that file in your editor of choice and set those lines to the following:

1
2
MODULES=(btrfs)
HOOKS=(base udev systemd autodetect keyboard modconf block sd-encrypt filesystems)

Once that is done, it is time to generate our new initramfs:

1
$ mkinitcpio -P

If you notice some warning messages about missing firmware, check the Additional Firmware section towards the end of this guide. It is okay to ignore for the moment and likely won’t cause any problems. Though if you are concerned, go ahead and check out that section then regenerate your initramfs after you install any additional firmware packages.

Bootloader

If you don’t want to use systemd-boot, follow instructions for your bootloader of choice instead. Though if you are cool with systemd-boot, go ahead and install it with the following command:

1
$ bootctl --path=/boot install

Now to make our Arch boot entry. You are going to need the UUID of your root partition for this, so to make things a bit easier, I would recommend adding the UUID to the boot entry file, twice:

Warning

Make sure you are using the correct drive and are specifying your root partition.

1
2
$ echo `blkid -S UUID -o value /dev/nvme1n1p2` >> /boot/loader/entries/arch.conf
$ echo `blkid -S UUID -o value /dev/nvme1n1p2` >> /boot/loader/entries/arch.conf

Go ahead and open /boot/loader/entries/arch.conf in your editor of choice and add the following:

Warning

If you did not use linux-zen, make sure you specify that in this file. Also, if you used amd-ucode, use that for your initrd instead.

1
2
3
4
5
6
7
title Arch Linux
linux /vmlinuz-linux-zen
initrd /intel-ucode.img
initrd /initramfs-linux-zen.img
options nvidia-drm.modeset=1 apparmor=1 security=apparmor rd.luks.name=<UUID>=luks root=/dev/mapper/luks rootflags=subvol=@
rd.luks.options=<UUID>=discard rw quiet lsm=lockdown,yama,apparmor,bpf

The nvidia-drm.modeset=1 kernel option will be needed for our NVIDIA driver, which we will install later. If you won’t be using the NVIDIA driver, go ahead and remove this.

GNOME 42

Installing the latest version of GNOME is pretty straight forward. At the time of writing, GNOME 42 is the latest and greatest! You can install it like this:

1
$ pacman -S gnome gdm gnome-tweaks firefox

You may want to include xorg if you are not planning on using Wayland and prefer to stick with X11.

Once that is done installing, you will want to enable to GNOME Desktop Manager service:

1
$ systemctl enable gdm

If you are looking to install a different Window Manager / Desktop Environment, check the Arch Wiki for guidance on how to do that.

NVIDIA

To install the NVIDIA proprietary drivers, and to get them to play nice with Wayland and GDM, we need to do a few things.

First, install the following:

1
$ pacman -S nvidia-dkms nvidia-utils

You may just want nvidia instead of nvidia-dkms, depending on your setup.

The next thing you’ll want to do is prevent the Nouveau video driver from loading. Open /etc/modprobe.d/prevent-nouveau.conf in your editor of choice, and add:

1
blacklist nouveau

As of writing this, GDM will force X11 and not offer Wayland as an option if it detects that an NVIDIA graphics card is present. To get around this, we just need to disable this rule by doing the following:

1
$ ln -s /dev/null /etc/udev/rules.d/61-gdm.conf

Now we will need to set some custom NVIDIA kernel flags and enable NVIDIA’s power management services in order for the latest drivers to work with Wayland. This is a recent issue, as I haven’t had to do this in the past.

Go ahead and open up /etc/modprobe.d/nvidia-power-management.conf in your editor of choice, and add the following:

1
options nvidia NVreg_EnableMSI=0 NVreg_PreserveVideoMemoryAllocations=1 NVreg_TemporaryFilePath=/tmp-nvidia

You can remove the NVreg_EnableMSI=0 option if you’d like. I needed to do that on my end to fix an issue where my display wouldn’t wake up after my computer went to sleep.

Now you just need to enable the following NVIDIA services:

1
2
$ systemctl enable nvidia-suspend
$ systemctl enable nvidia-hibernate

Since we added some new configuration files to our /etc/modprobe.d/ folder, we will need to regenerate our initramfs. Earlier we included the modconf hook, which will tell mkinitcpio to include all configuration files in that directory.

1
$ mkinitcpio -P

Once the initframfs generates, it is time to boot into our new install for real! Go ahead and exit chroot, unmount, and reboot:

1
2
3
$ exit
$ umount -a
$ reboot

Apparmor Profiles

Assuming everything went well, you should now be booted into your fresh Arch install and logged into your local user account :3

Now that we’re here, let’s verify that Apparmor is working.

1
2
$ sudo aa-enabled
Yes

The output of Yes will indicate that Apparmor is enabled. To get the current status and list of profiles loaded, run the following:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
$ sudo aa-status
apparmor module is loaded.
50 profiles are loaded.
50 profiles are in enforce mode.
   ...
	...
0 profiles are in complain mode.
0 profiles are in kill mode.
0 profiles are in unconfined mode.
0 processes have profiles defined.
0 processes are in enforce mode.
0 processes are in complain mode.
0 processes are unconfined but have a profile defined.
0 processes are in mixed mode.
0 processes are in kill mode.

If you aren’t getting output similar to what is outlined above, head on over to the Arch Wiki for guidance.

Timeshift

Timeshift will be the application we use to create and manage BTRFS snapshots of our root subvolume. This application isn’t yet available in Arch’s official repos, though like anything else, we can find it in the Arch User Repository (AUR).

An easy and convenient way to install and manage AUR packages on your system is to use an AUR helper (or write your own ;3). There are several helpers to pick from, though today I will be using paru. One of the main reasons I like paru is that it allows you to review and edit the PKGBUILD, in-line, before it runs. If you don’t care about that, or don’t enjoy using vim to edit files, I’d suggest checking out yay.

1
2
3
$ git clone https://aur.archlinux.org/packages/paru/
$ cd paru
$ makepkg -si

Now that you have paru, you can install Timeshift like this:

1
$ paru -S timeshift

Tip

Think about your disaster and data recovery plan and test it often!

After you install Timeshift, go ahead and open it up and go through the settings / setup process. Make sure you select the BTRFS snapshot type. The snapshot location is up to you, though the most convenient is probably to have it stored on the root subvolume.

I am only using snapshots on my root subvolume, though you can enable snapshots on your home subvolume as well. I am using Timeshift for my system level stuff and duplicity for my user data.

Update 24 Aug 2022: It turns out that by default, the cronie service is disabled in Arch. This is needed in order for Timeshift to do automatic snapshots. To fix this, run the following to enable the service:

1
$ systemctl enable --now cronie.service

TPM 2.0 Enrollment

Warning

Depending on your security situation, doing this might be a bad idea.

I liked the idea of having my TPM unlock the LUKS volume on boot to speed up the boot process, creating a more seamless experience. Depending on your situation, enrolling your TPM as one of your LUKS key slots might be a bad idea. As always, consider your threat model.

Verify that your TPM is detected, and that it is version 2.0:

1
2
$ cat /sys/class/tpm/tpm0/tpm_version_major 
2

If that path doesn’t exist, you might be able to check /sys/class/tpm/tpm0/device/description.

At this point, systemd-cryptenroll should already be able to see our TPM, since we included the systemd and sd-encrypt hooks when generating our initramfs. To verify this you can run:

1
2
3
4
5
$ systemd-cryptenroll --tpm2-device=list

PATH        DEVICE DRIVER 
/dev/tpmrm0 00:00  tpm_tis

You should see your TPM listed in the output, similar to what is shown above. Assuming that looks good, you will now want to enroll your TPM to one of your key slots on your LUKS volume, in my case that is partition 2:

1
$ systemd-cryptenroll --tpm2-device=auto --tpm2-pcrs=0,7 /dev/nvme1n1p2

After that, you will need to add a new rd.luks.options option to your boot entry for Arch. Open /boot/loader/entries/arch.conf in your editor of choice, and add tpm2-device=auto. Your boot entry should now look something like this:

1
2
3
4
5
6
title Arch Linux
linux /vmlinuz-linux-zen
initrd /intel-ucode.img
initrd /initramfs-linux-zen.img
options nvidia-drm.modeset=1 apparmor=1 security=apparmor rd.luks.name=<UUID>=luks root=/dev/mapper/luks rootflags=subvol=@
rd.luks.options=<UUID>=tpm2-device=auto,discard rw quiet lsm=lockdown,yama,apparmor,bpf

If you reboot, you should see your system go straight to the login screen. If the TPM is tampered with or removed, you will be prompted to enter your passphrase like before.

Secure Boot

Before you enable Secure Boot, you will need to go into your BIOS and enable “Setup Mode”. This will allow key enrollment. Depending on your BIOS, you may have to first delete all of your pre-existing keys before Setup Mode can be toggled. Refer to your motherboard’s documentation if you run into issues.

You may also wish to keep vendor keys enrolled so you can continue to boot into Windows and use other boot utilities. If you don’t know what you are doing, chances are you will want to keep these vendor keys.

Once your BIOS is in Secure Boot Setup Mode, go ahead and boot back into Arch, and run the following command to verify:

1
2
3
4
$ sbctl status
Installed:	✓ sbctl is installed
Setup Mode:	✗ Enabled
Secure Boot:	✗ Disabled

You should see output similar to what is shown above. The important part is to make sure that “Setup Mode” says enabled and that “Secure Boot” says disabled.

Now, with that out of the way, you can create secure boot keys and enroll them, then sign your kernel files. To do this, I used sbctl. They have some excellent documentation on their Github repo.

To create keys and enroll them, do the following (note, this will ensure that Microsoft’s keys are also enrolled. Remove --microsoft if you don’t want that):

1
2
$ sbctl create-keys
$ sbctl enroll-keys --microsoft

Now we need to sign our EFI boot files. To figure out which files need signing, we can run sbctl verify:

1
2
3
4
5
6
$ sbctl verify
Verifying file database and EFI images in /efi...
✘ /boot/EFI/BOOT/BOOTX64.EFI is not signed
✘ /boot/EFI/systemd/systemd-bootx64.efi  is not signed
✘ /boot/vmlinuz-linux is not signed
✘ /boot/vmlinuz-linux-zen is not signed

To sign these files, simply run:

1
2
3
4
$ sbctl sign -s /boot/EFI/BOOT/BOOTX64.EFI
$ sbctl sign -s /boot/EFI/systemd/systemd-bootx64.efi 
$ sbctl sign -s /boot/vmlinuz-linux
$ sbctl sign -s /boot/vmlinuz-linux-zen

To verify that those files were signed, run sbctl verify again:

1
2
3
4
5
6
$ sbctl verify
Verifying file database and EFI images in /efi...
✔ /boot/EFI/BOOT/BOOTX64.EFI is signed
✔ /boot/EFI/systemd/systemd-bootx64.efi  is signed
✔ /boot/vmlinuz-linux is signed
✔ /boot/vmlinuz-linux-zen is signed

You may also need to generate an EFI Stub, depending on your configuration. I didn’t need to do this on my end in order for this to work. To learn more, check out the sbctl usage guide.

Now you can reboot and enable Secure Boot! Once you log back in, you can verify that everything is working by running sbctl status:

1
2
3
4
5
6
7
$ sbctl status
Installed:	✓ sbctl is installed
Owner GUID:	<UUID>
Setup Mode:	✓ Disabled
Secure Boot:	✓ Enabled
Vendor Keys:	microsoft

Congrats, you now have Secure Boot working!

Additional Firmware

The linux-firmware package covers most firmware for common hardware devices, though I had to install some additional packages to ensure that my system was stable. When you generate your initframfs, you may see some warning messages about firmware not being installed. Search around for the firmware that is mentioned and you will likely find a package for it in the AUR.

1
2
$ pacman -S linux-firmware-qlogic linux-firmware-iwlwifi-git
$ paru -S upd72020x-fw aic94xx-firmware wd719x-firmware

At this point you’re done! You can probably start enjoying your new Arch Linux install, so go have fun :3

References

I used so, so many references during this process. The list below are the ones I found to be the most helpful.

<3 m0x