⚠️ This repository, along my other not-dead projects, was moved to Codeberg. Please head over there for the further development.
This guide is a WIP, there might be errors, typos. Further steps will be added.
Going over the years, I installed Arch linux countless of times, thriving for the setup that's perfect for me. I'm still not there, and there's still much to optimize but I wanted to jot down what I have so far. There's a saying in Turkish: "zevkler ve renkler tartisilmaz", it means one cannot discuss over tastes and colors, since they're subjective, which is why I'm mainly focused on the functional side of things here. Although there'll still be some styling involved.
Note that I'll be assuming you're already confident with linux (esp. Arch linux in particular) and won't be giving detailed explanations on how to create, format and mount disk partitions, enable or start systemd services etc.
I'd definitely recommend informing yourself on the basics of LUKS, TPM, Secure Boot and BTRFS beforehand.
I'm working on a Lenovo ThinkPad T460p with following specs:
- CPU: Intel Core i5-6440HQ (4) @ 3,50 GHz
- GPU: Intel HD Graphics 530 @ 0,95 GHz [Integrated]
- Memory: 16 GB (2x8)
- Disk: 256 GB intenso SATA III SSD (550Mb/s R + 500Mb/s W)
- Display: 1920x1080 in 14", 60 Hz eDP
I started writing this on 13.10.2025 and as of today, I'm on Linux kernel 6.17.1-arch1-1.
Given the fact that I'm working on a laptop, I'd been wanting to take advantage of it's security functions for a while. In this day and age, protecting your data keeps getting harder and harder. Due to Intel Management Engine, standart procedures do not protect you against the government, but I'd like to think that I'm at least safe from potential not-so-qualified civilian adversaries.
Here's what was important for me:
- LUKS for almost full disk encryption: almost because I'm keeping the EFI partition unencrypted.
- TPM2 enrollment of the decryption keys of LUKS: because I don't want to enter two passwords everytime I boot.
- Secure boot: in order to protect those keys in TPM2 with a couple of TPM PCRs.
- Snapper/timeshift: I'm not very responsible when it comes to backups and snapshots but that's something I want to change, which is more realistic if taking a snapshot is easier, so I wanted my system to be at least snapshot-ready.
With these in mind, my choice was to go with EFI + LUKS partition and a BTRFS system on the LUKS partition. It might be worth mentioning that my setup is UEFI only without CSM support.
The best resource for guides, tips, tricks and troubleshooting (even for other distros) and one of the biggest reasons I love Arch linux so much, is of course the Arch wiki. I will make a lot of references to the Arch wiki installation guide here.
Another resource I found really helpful was Arch Install with Secure Boot, btrfs, TPM2 LUKS encryption, Unified Kernel Images by Walian's Tech/Linux Blog. It was a great starting point for me so I'd also like to thank them here.
Well then, let's dive in.
Complete steps 1.1 Acquire an installation image to 1.8 Update the system clock according to the guide.
I decided to create the following partition table.
| NAME | TYPE | PARTITION TYPE | SIZE | MOUNTPOINT | |||
|---|---|---|---|---|---|---|---|
| sda | disk | 232,9G | |||||
| └─ | sda1 | partition | EFI System (typecode 1) | 1G | /efi | ||
| └─ | sda2 | partition | Linux filesystem (typecode 20) | 231,9G | |||
| └─ | linuxroot | crypt | |||||
| └─ | @ | btrfs subvolume | / | ||||
| └─ | @home | btrfs subvolume | /home | ||||
| └─ | @var_cache | btrfs subvolume | /var/cache | ||||
| └─ | @var_log | btrfs subvolume | /var/log | ||||
| └─ | @var_spool | btrfs subvolume | /var/spool | ||||
| └─ | @var_tmp | btrfs subvolume | /var/tmp | ||||
| └─ | @swap | btrfs subvolume | /swap | ||||
Since @swap is not a partition but rather a subvolume, we'll use a swapfile,
which will have a preallocated size, defined through BTRFS configuration and it's
therefore different than a physical block size.
Before actually partitioning your disk, it is wise to apply one of the methods listed in the wiki for a secure erasure of the drive in case you're reinstalling on a used drive.
Create an EFI partition (I recommend 1GiB) and a Linux filesystem with your desired size.
Afterwards, encrypt the non-boot partition using cryptsetup and open
the container. I named my container linuxroot because I've seen the same name
in a lot of resources but it really doesn't matter much.
$ cryptsetup luksFormat /dev/sda2
$ cryptsetup open /dev/sda2 linuxrootCreate filesystems on both boot and non-boot partitions.
$ mkfs.fat -F32 -n EFI /dev/sda1
$ mkfs.btrfs -f -L linuxroot /dev/mapper/linuxrootNow mount the non-boot partition. Do not mount the boot partition on /mnt/boot or
/mnt/efi because /mnt won't be our root partition.
$ mount /dev/mapper/linuxroot /mntAlso create the btrfs subvolumes. In order to make the subvolumes more suitable for snapper, I followed the suggested filesystem layout. It is important to note that it is now necessary to generate fstab because systemd cannot automatically detect our subvolume names.
$ btrfs subvolume create /mnt/@
$ btrfs subvolume create /mnt/@home
$ btrfs subvolume create /mnt/@var_cache
$ btrfs subvolume create /mnt/@var_log
$ btrfs subvolume create /mnt/@var_spool
$ btrfs subvolume create /mnt/@var_tmp
$ btrfs subvolume create /mnt/@swapNow unmount the top-level subvolume (the btrfs container is also a subvolume) and mount the @ subvolume on /mnt, because that will be our root.
$ umount /mnt
$ mount -o subvol=@ /dev/mapper/linuxroot /mntNow create the required directories and mount the respective subvolumes as well as the ESP.
$ mkdir /mnt/efi
$ mount /dev/sda1 /mnt/efi
$ mkdir /mnt/home
$ mount -o subvol=@home /dev/mapper/linuxroot /mnt/home
$ mkdir -p /mnt/var/cache
$ mount -o subvol=@var_cache /dev/mapper/linuxroot /mnt/var/cache
$ mkdir /mnt/var/log
$ mount -o subvol=@var_log /dev/mapper/linuxroot /mnt/var/cache
$ mkdir /mnt/var/spool
$ mount -o subvol=@var_spool /dev/mapper/linuxroot /mnt/var/spool
$ mkdir /mnt/var/tmp
$ mount -o subvol=@var_tmp /dev/mapper/linuxroot /mnt/var/tmp
$ mkdir /mnt/swap
$ mount -o subvol=@swap /dev/mapper/linuxroot /mnt/swapWe need to create a swapfile, keeping in mind the swapfile and btrfs restrictions. You can read more about them on the relevant arch wiki section and in the BTRFS docs on swapfile.
Relevant for my case were the following two points:
- a subvolume containing an active swapfile cannot be snapshotted
- the file must be preallocated
Btrfs has a one-liner to create a swapfile:
$ btrfs filesystem mkswapfile --size 18g --uuid clear /mnt/swap/swapfileI like enough space for hibernation so I chose my size accordingly. As for the clear UUID, the btrfs documentation mentions the following:
Please note that the UUID returned by the mkswap utility identifies the swap “filesystem” and because it’s stored in a file, it’s not generally visible and usable as an identifier unlike if it was on a block device.
We can now activate the swapfile as well.
$ swapon /mnt/swap/swapfileComplete the steps under 2. Installation. In addition to the kernel, firmware and programs of your choosing, install the following packages as well:
- cryptsetup
- btrfs-progs
- sbctl
- snapper
I personally like to install some of the essentials directly with pacstrap as well, giving me the following list:
- base
- base-devel
- btrfs-progs
- cryptsetup
- devtools
- efibootmgr
- exfat-utils
- fwupd
- gnome-keyring
- intel-ucode
- linux
- linux firmware
- lshw
- man-db
- man-pages
- neovim
- networkmanager
- sbctl
- snap-pac
- snapper
- udiskie
- unzip
- zip
- zsh
Complete steps 3.1 fstab to 3.5 Network configuration like you would normally do.
Before we regenerate the initramfs, we need to configure mkinitcpio and kernel params.
Let's start with mkinitcpio, by now you should be in chroot, so edit
/etc/mkinitcpio.conf and uncomment the HOOKS line that creates a systemd based
initramfs which loads an encrypted root filesystem. It should look like this:
HOOKS=(base systemd autodetect microcode modconf kms keyboard sd-vconsole sd-encrypt block filesystems fsck)
This will load the necessary modules.
Next, edit the /etc/mkinitcpio.d/linux.preset file to tell mkinitcpio to
make UKIs instead of the
standalone kernel images. We also want mkinitcpio to source our mkinitcpio.conf file.
Comment out the default_image=... and fallback_image=... lines and uncomment the
default_uki=... and fallback_uki=... lines instead. Also uncomment the
ALL_config=... line in the top.
The final result should look like this:
# mkinitcpio preset file for the 'linux' package
ALL_config="/etc/mkinitcpio.conf"
ALL_kver="/boot/vmlinuz-linux"
PRESETS=('default' 'fallback')
#default_config="/etc/mkinitcpio.conf"
#default_image="/boot/initramfs-linux.img"
default_uki="/efi/EFI/Linux/arch-linux.efi"
#default_options="--splash /usr/share/systemd/bootctl/splash-arch.bmp"
#fallback_config="/etc/mkinitcpio.conf"
#fallback_image="/boot/initramfs-linux-fallback.img"
fallback_uki="/efi/EFI/Linux/arch-linux-fallback.efi"
fallback_options="-S autodetect"
I'd recommend informing yourself about the UKI generation process to make further adjustments tailored to your needs, but this is a good start.
Next, we have to configure our boot loader to unlock the encrypted non-boot partition at boot.
I don't like putting all kernel parameters in one file so I put created the file
/etc/cmdline.d/root.conf and put the following line in there:
rd.luks.name=DEVICE-UUID=linuxroot rd.luks.options=timeout=90s,discard,password-echo=no,tries=3 root=/dev/mapper/linuxroot rootfstype=btrfs rootflags=subvol=@ rw
whereas DEVICE-UUID is the UUID of your LUKS partition, which you can get using blkid.
$ blkid
...
/dev/sda2: UUID="XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX" TYPE="crypto_LUKS" PARTUUID="XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX"
...Make sure to copy the UUID parameter and not the PART-UUID.
Now that we have the necessary configurations in place, we can generate the UKIs.
$ mkinitcpio -PYou can check /efi for the newly generated UKIs.
$ ls -lR /efi
/efi:
total 4
drwx------ 5 root root 4096 Oct 12 02:22 EFI
/efi/EFI:
total 4
drwx------ 2 root root 4096 Oct 13 22:29 Linux
/efi/EFI/Linux:
total 266496
-rw------- 1 root root 232310328 Oct 13 22:29 arch-linux-fallback.efi
-rw------- 1 root root 40576568 Oct 13 22:29 arch-linux.efiYou should be able to see the UKIs arch-linux-fallback.efi and arch-linux.efi.
Set your root password as always.
$ passwdLet's install the boot loader.
For my purposes, systemd-boot is more than enough, even though I like grub, it's unlikely for me to install another operating system on this device. You can also enable some default services like NetworkManager, timesycnd etc. at this point.
$ systemctl enable NetworkManager systemd-resolved systemd-timesyncd
$ systemctl mask systemd-networkd
$ bootctl install --esp-path=/efiNow we can reboot into UEFI settings and enable setup mode for secure boot in order to finish up our installation.
$ sync
$ systemctl reboot --firmware-setupDon't forget to put secure boot into setup mode before proceding. Do not enable secure boot yet.
Assuming nothing went wrong, you should be able to reboot into Arch and you should be prompted to enter the LUKS encryption key. If not, this is the perfect time to troubleshoot.
First, check that secure boot of your firmware is recognized and setup mode is actually enabled:
$ sbctl statusMake sure that your output contains Setup Mode: ✗ Enabled.
Now you can create and enroll the secure boot keys. I enrolled the Microsoft vendor keys just for
safety. If you're sure your device doesn't have any OPROMs signed by Microsoft, you can leave
-m option out while enrolling your keys. Check out the
sbctl wiki
for more info.
$ sbctl create-keys
$ sbctl enroll-keys -mIt's time to sign our boot loader UEFI executables as well as our UKIs.
$ sbctl sign -s -o /usr/lib/systemd/boot/efi/systemd-bootx64.efi.signed /usr/lib/systemd/boot/efi/systemd-bootx64.efi
$ sbctl sign -s /efi/EFI/BOOT/BOOTX64.EFI
$ sbctl sign -s /efi/EFI/Linux/arch-linux.efi
$ sbctl sign -s /efi/EFI/Linux/arch-linux-fallback.efiThe option -o saves the signed UEFI executable under a different name instead of overwriting it.
See
Systemd-boot §3.3 Signing for Secure Boot
for more info.
Option -s adds the files in sbctl database to automatically resign the images when required.
You can test it out by reinstalling the kernel.
$ pacman -S linux
...
(4/5) Signing EFI binaries...
Generating EFI bundles....
File has already been signed /efi/EFI/BOOT/BOOTX64.EFI
File has already been signed /efi/EFI/Linux/arch-linux-fallback.efi
File has already been signed /efi/EFI/Linux/arch-linux.efi
File has already been signed /usr/lib/systemd/boot/efi/systemd-bootx64.efi.signed
...In walian's guide, the UKIs are resigned in this step but my sbctl decided there was no change, hence no reason to resign. An explanation of this discrepancy might be an update to sbctl, I'm not sure. The important thing is, sbctl is being called and there's an attempt to resign these four files.
You can now reboot your system to save our changes.
First, we will generate a recovery key. You should make a safe and hidden copy of this key, because we're going to revoke the passphrase you entered whilst creating the LUKS volume in order to avoid redundancy.
$ systemd-cryptenroll /dev/sda2 --recovery-keyNow, create a key that is bound to the TPM PCRs of your liking. I chose the following TPM PCRs:
- TPM PCR 7: Secure Boot state; changes when UEFI SecureBoot mode is enabled/disabled, or firmware certificates (PK, KEK, db, dbx, ...) changes.
There have been multiple reports regarding security issues of auto-unlocking LUKS with TPM2. Here's an interesting article on this subject. I decided to use TPM with PIN.
Setup TPM2 with
$ sudo systemd-cryptenroll --tpm2-device=auto --tpm2-pcrs=7 --tpm2-with-pin=yes