My personally incomprehensible Arch Linux installation guide. WIP, will become a POSIX shell script. Not for those who like using their computers.
Find a file
2026-05-25 00:31:06 +00:00
README.md Update README.md 2026-05-25 00:31:06 +00:00
simple.txt Upload files to "/" 2026-01-24 03:25:36 +00:00
simpler.txt Upload files to "/" 2026-01-24 03:25:36 +00:00

Arch Linux Encrypted Dual Drive Installation Guide

A comprehensive guide for installing Arch Linux on a UEFI system with full-disk encryption, Btrfs, Secure Boot, and TPM 2.0 auto-unlock.


Overview

Target Configuration:

  • NVMe drive (${ROOT_DISK}): Encrypted system partition with Btrfs subvolumes
  • SATA SSD (${DATA_DISK}): Encrypted data storage for media files
  • Boot method: UEFI direct boot (EFISTUB) with Unified Kernel Image
  • Kernel: linux-hardened (security-hardened kernel with enhanced ASLR and compile-time hardening)
  • Optional features: Secure Boot, TPM 2.0 auto-unlock, DNS-over-TLS, AppArmor

Both encrypted partitions unlock at boot with a single passphrase via systemd-cryptsetup auto-unlock and optional TPM.

Prerequisites:

  • UEFI firmware
  • If using Secure Boot: set firmware to Setup Mode before beginning
  • If using TPM: ensure TPM 2.0 support
  • Single disk installations: ignore all commands referencing ${DATA_DISK}

Back up all important data before proceeding.


Table of Contents

Part 1: Installation

  1. Prepare the Live Environment
  2. Partition the Disks
  3. Encrypt and Format
  4. Create Btrfs Subvolumes
  5. Mount Filesystems
  6. Install Base System
  7. Configure the System
  8. Configure Networking
  9. Configure Boot
  10. Secure Boot Setup (Optional)
  11. Reboot
  12. TPM Enrollment (Optional)
  13. Post-Installation Setup

Part 2: System Hardening and Optimization

  1. Security Hardening
  2. Performance Tuning
  3. Hardware Optimization
  4. Audio Configuration
  5. Power Management
  6. Optional Enhancements

Appendices


Part 1: Installation

1. Prepare the Live Environment

1.1 Set Keyboard Layout

loadkeys br-abnt2

Replace br-abnt2 with the appropriate layout.

1.2 Connect to Wi-Fi

Identify the wireless interface:

ip link

Unblock Wi-Fi and bring the interface up:

rfkill unblock wifi
ip link set wlan0 up

Replace wlan0 with the actual interface name.

Connect using iwctl:

iwctl

Inside iwctl:

station wlan0 scan
station wlan0 get-networks
station wlan0 connect YOUR_WIFI_SSID
exit

Verify connectivity:

ping -c3 archlinux.org

1.3 Synchronize System Clock

timedatectl set-ntp true

2. Partition the Disks

2.1 Identify and Export Disk Paths

lsblk

Export drive paths:

export ROOT_DISK="/dev/nvme0n1"
export DATA_DISK="/dev/sda"

Note: NVMe partition references use p notation (${ROOT_DISK}p1, ${ROOT_DISK}p2). SATA/SCSI references do not (${DATA_DISK}1).

2.2 Wipe Existing Partition Tables

sgdisk -Z ${ROOT_DISK}
sgdisk -Z ${DATA_DISK}

2.3 Partition the NVMe (System Disk)

gdisk ${ROOT_DISK}
o
Y
n
<Enter>
<Enter>
+512M
ef00
c
ESP
n
<Enter>
<Enter>
<Enter>
8304
c
2
ROOT
w
Y

2.4 Partition the SATA SSD (Data Disk)

gdisk ${DATA_DISK}
o
Y
n
<Enter>
<Enter>
<Enter>
8309
c
DATA
w
Y

Partition types: ef00 is the EFI System Partition. 8304 is Linux root (x86-64), the correct type for the encrypted system partition per the Discoverable Partitions Specification (DPS) — systemd-gpt-auto-generator and related tooling use this type to identify the root partition's role. 8309 is the generic Linux LUKS type used for the data disk, which has no DPS-governed role.

2.5 Reload Partition Tables

partprobe -s ${ROOT_DISK}
partprobe -s ${DATA_DISK}

3. Encrypt and Format

3.1 Wipe Drives Before Encryption

For NVMe drives, perform a cryptographic erase using the drive controller. Install the required tool from the live environment:

pacman -Sy nvme-cli

Then erase:

nvme format --ses=1 ${ROOT_DISK}

This resets the drive's internal encryption key, rendering all existing data cryptographically inaccessible. It completes in seconds regardless of drive capacity.

For SATA SSDs, do not overwrite with random data if TRIM will be used — the first TRIM command will undo any overwrite. Proceed directly to formatting; the LUKS encryption layer makes prior data cryptographically inaccessible.

3.2 Encrypt Partitions

Encrypt the root partition:

cryptsetup luksFormat --sector-size 4096 ${ROOT_DISK}p2
cryptsetup open ${ROOT_DISK}p2 cryptroot

Encrypt the data partition:

cryptsetup luksFormat --sector-size 4096 ${DATA_DISK}1
cryptsetup open ${DATA_DISK}1 cryptdata

--sector-size 4096 aligns the LUKS container to 4096-byte sectors. Many consumer SSDs misreport 512-byte logical sectors despite having 4096-byte physical sectors; this flag ensures correct alignment regardless. The --hash flag is intentionally omitted: in LUKS2 with argon2id, --hash controls only the AF-splitter digest used to protect key material, not argon2id's internal function (which is BLAKE2b regardless). SHA-256 provides sufficient collision resistance for this purpose; SHA-512 adds no meaningful security.

Note: Performance flags (no-read-workqueue, no-write-workqueue, discard) are set in /etc/crypttab.initramfs (see Section 9.6), which is the authoritative source for boot-time behavior.

3.3 Verify the LUKS Containers

cryptsetup luksDump ${ROOT_DISK}p2
cryptsetup luksDump ${DATA_DISK}1

Confirm: LUKS version 2, cipher aes-xts-plain64, PBKDF argon2id, sector size 4096.

3.4 Back Up LUKS Headers

The LUKS header stores the encrypted master key. Its loss means permanent data loss regardless of knowing the passphrase.

cryptsetup luksHeaderBackup ${ROOT_DISK}p2 --header-backup-file cryptroot-header.img
cryptsetup luksHeaderBackup ${DATA_DISK}1 --header-backup-file cryptdata-header.img

Store these files on offline physical media, separately from the passphrase.

3.5 Format Filesystems

ESP:

mkfs.fat -F32 -n ESP ${ROOT_DISK}p1

Root partition:

mkfs.btrfs -L ROOT /dev/mapper/cryptroot

Data partition:

mkfs.btrfs -L DATA /dev/mapper/cryptdata

Note on checksums: Btrfs uses crc32c by default, which is sufficient for error detection given the dm-crypt encryption layer above it. Since kernel 5.5, xxhash is available via mkfs.btrfs --checksum xxhash. It is both faster than crc32c and has better collision resistance, with no compatibility drawbacks on kernels ≥ 5.5. The tradeoff is that the filesystem cannot be mounted on older kernels or tools. This choice is irreversible and must be made at format time.


4. Create Btrfs Subvolumes

Mount the root filesystem temporarily:

mount /dev/mapper/cryptroot /mnt

Create subvolumes using a flat layout:

btrfs subvolume create /mnt/@
btrfs subvolume create /mnt/@home
btrfs subvolume create /mnt/@var
btrfs subvolume create /mnt/@snapshots

Unmount:

umount /mnt

See Appendix A for subvolume layout details.


5. Mount Filesystems

5.1 Understanding Btrfs Mount Options

Most Btrfs-specific options are filesystem-wide: they apply from the first mount and are inherited by all subsequent subvolume mounts. noatime is a VFS-level option and must be specified per-mount. discard=async and space_cache=v2 are both kernel defaults and do not need to be specified. See Appendix A.2.

5.2 Mount Root Subvolume

mount -o noatime,compress=zstd:2,subvol=@ /dev/mapper/cryptroot /mnt

5.3 Create Mount Points

mkdir -p /mnt/{home,var,snapshots,efi,data}

5.4 Mount Remaining Subvolumes

mount -o noatime,subvol=@home /dev/mapper/cryptroot /mnt/home
mount -o noatime,subvol=@var /dev/mapper/cryptroot /mnt/var
mount -o noatime,subvol=@snapshots /dev/mapper/cryptroot /mnt/snapshots

5.5 Mount ESP and Data

mount ${ROOT_DISK}p1 /mnt/efi
mount -o noatime,compress=zstd:2 /dev/mapper/cryptdata /mnt/data

6. Install Base System

6.1 Update Mirrorlist

reflector -c "XX,YY" -f 5 -a 24 -p https --save /etc/pacman.d/mirrorlist --verbose

Replace XX,YY with the country codes for the two countries geographically closest to the system's location. The -c flag restricts mirrors to those countries, reducing latency. -f 5 selects the five fastest mirrors, -a 24 excludes mirrors not synchronized within 24 hours, and -p https restricts to HTTPS mirrors.

6.2 Install Packages

pacstrap -i -K /mnt base base-devel linux-hardened linux-hardened-headers linux-firmware \
    cryptsetup efibootmgr amd-ucode btrfs-progs sof-firmware \
    iwd systemd-resolvconf chrony wireless-regdb iptables-nft nftables \
    sbctl tpm2-tools tpm2-tss zram-generator apparmor vim git bash-completion

Kernel alternatives: linux-hardened provides a security-hardened kernel with enhanced ASLR, more restrictive compile-time defaults, and an additional kernel patch set. For a performance-oriented kernel without these restrictions, replace with linux-zen.

Package variations:

  • Intel CPU: replace amd-ucode with intel-ucode
  • Without Secure Boot: omit sbctl
  • Without TPM: omit tpm2-tools and tpm2-tss
  • Without firewall: omit iptables-nft and nftables
  • Without AppArmor: omit apparmor

See Appendix B for package explanations.

6.3 Generate fstab

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

Verify the output and make the following corrections manually:

  • Confirm no discard option appears on Btrfs entries (Btrfs async discard is a kernel default; see Appendix A.3)
  • Add noexec,nodev,nosuid,nosymfollow,fmask=0137,dmask=0027 to the ESP entry options (genfstab includes neither):
    • noexec,nodev,nosuid,nosymfollow: required by the Boot Loader Specification for all ESP mounts from Linux; prevent execution of files from the FAT32 partition, block device file interpretation, setuid escalation, and symlink traversal attacks
    • fmask=0137: files on the ESP get permissions rw-r----- — owner (root) can read/write, group can read, others have no access
    • dmask=0027: directories get rwxr-x--- — owner has full access, group can traverse and list, others have no access
  • Confirm ESP pass field is 2; all other entries should be 0

6.4 Preload VFAT Modules

Create a module preload configuration to ensure ESP filesystem modules are available, preventing boot failures after kernel updates:

cat > /mnt/etc/modules-load.d/vfat.conf << 'EOF'
vfat
nls_cp437
nls_ascii
EOF

7. Configure the System

Enter the chroot environment:

arch-chroot /mnt bash

7.1 Timezone and Clock

ln -sf /usr/share/zoneinfo/Region/City /etc/localtime
hwclock --systohc

7.2 Locale

vim /etc/locale.gen

Uncomment the desired locale (e.g., en_US.UTF-8 UTF-8), then generate:

locale-gen
cat > /etc/locale.conf << 'EOF'
LANG=en_US.UTF-8
LC_COLLATE=C
EOF

7.3 Console Keymap

echo "KEYMAP=xx" > /etc/vconsole.conf

Replace xx with the keymap matching the keyboard layout used in the live environment (Section 1.1). Available keymaps can be listed with localectl list-keymaps.

7.4 Hostname and Hosts

echo "arch" > /etc/hostname
cat > /etc/hosts << 'EOF'
127.0.0.1    localhost
127.0.1.1    arch.localdomain arch
::1          localhost ip6-localhost ip6-loopback
ff02::1      ip6-allnodes
ff02::2      ip6-allrouters
EOF

Replace arch with the configured hostname.

7.5 Password Hashing Strength

Configure before creating user accounts:

vim /etc/login.defs

Find and set:

YESCRYPT_COST_FACTOR 8

Note: This applies only to passwords set after this change. Existing passwords must be reset with passwd username to use the new cost factor.

7.6 Create User

useradd -mG wheel username
passwd username
passwd

Enable sudo for the wheel group:

EDITOR=vim visudo

Uncomment:

%wheel ALL=(ALL:ALL) ALL

8. Configure Networking

8.1 Enable Network Services

systemctl enable systemd-networkd systemd-resolved iwd nftables chronyd apparmor auditd

Services enabled:

  • systemd-networkd: network management (DHCP, interface configuration)
  • systemd-resolved: DNS resolver with DNSSEC and DNS-over-TLS
  • iwd: wireless daemon
  • nftables: stateful packet filter firewall
  • chronyd: NTP/NTS time synchronization
  • apparmor: mandatory access control
  • auditd: kernel audit daemon; logs privileged operations and AppArmor denials to /var/log/audit/audit.log

8.2 iwd Configuration

mkdir -p /etc/iwd
cat > /etc/iwd/main.conf << 'EOF'
[General]
AddressRandomization=network
AddressRandomizationRange=full
DisableANQP=true

[DriverQuirks]
PowerSaveDisable=*
EOF

Settings:

  • AddressRandomization=network: generates a stable MAC per SSID derived from the SSID and permanent adapter address — the same MAC is used on reconnection to a known network, preventing tracking while maintaining DHCP lease stability
  • AddressRandomizationRange=full: randomizes all six octets and sets the locally-administered bit
  • DisableANQP=true: disables Hotspot 2.0 ANQP queries that passively disclose network preferences
  • PowerSaveDisable=*: disables power saving for all adapters (improves stability)

Note: EnableNetworkConfiguration is intentionally absent (defaults to disabled). systemd-networkd manages all IP configuration. The journal entry station: Network configuration is disabled is expected and correct.

8.3 Network Configuration

Wi-Fi:

cat > /etc/systemd/network/20-wifi.network << 'EOF'
[Match]
Type=wlan

[Network]
DHCP=yes
IPv6PrivacyExtensions=true
IPv6LinkLocalAddressGenerationMode=stable-privacy
IgnoreCarrierLoss=3s
LLDP=false
EmitLLDP=false

[DHCPv4]
Anonymize=true
UseDomains=false

[DHCPv6]
UseDomains=false
EOF

Ethernet:

cat > /etc/systemd/network/20-wired.network << 'EOF'
[Match]
Type=ether
Kind=!*

[Link]
RequiredForOnline=no

[Network]
DHCP=yes
IPv6PrivacyExtensions=true
IPv6LinkLocalAddressGenerationMode=stable-privacy
LLDP=false
EmitLLDP=false

[DHCPv4]
Anonymize=true
UseDomains=false

[DHCPv6]
UseDomains=false
EOF

MAC Randomization (wired):

cat > /etc/systemd/network/01-mac.link << 'EOF'
[Match]
Type=ether
Kind=!*

[Link]
MACAddressPolicy=random
EOF

Settings:

  • Kind=!* in the wired match and link file: excludes virtual Ethernet interfaces (bridges, bonds, libvirt NAT interfaces, VLANs) which should not be managed as physical links
  • RequiredForOnline=no: prevents a 2-minute boot delay when the wired interface is not connected
  • IPv6LinkLocalAddressGenerationMode=stable-privacy: generates a stable but non-trackable link-local IPv6 address; without this, the link-local address is derived from the hardware MAC (EUI-64), exposing the permanent address in every IPv6 packet regardless of IPv6PrivacyExtensions
  • LLDP=false / EmitLLDP=false: disables Link Layer Discovery Protocol reception and transmission, preventing passive network topology disclosure
  • UseDomains=false: prevents DHCP-pushed DNS search domains from being registered as routing domains or overriding configured DNS

Note: iwd handles wireless MAC randomization natively via AddressRandomization in main.conf. The .link file is scoped to wired only — attempting to apply MACAddressPolicy to a wireless interface managed by iwd fails with a "Device or resource busy" error because iwd claims the interface before udev can act on it.

8.4 Wireless Regulatory Domain

vim /etc/conf.d/wireless-regdom

Uncomment the appropriate country code:

WIRELESS_REGDOM="XX"

Note: Setting the correct country code restricts the wireless driver to legal channel and transmit power limits for the jurisdiction. An incorrect or absent setting may permit operation on illegal frequencies or at illegal power levels.

8.5 DNS-over-TLS (systemd-resolved)

mkdir -p /etc/systemd/resolved.conf.d
cat > /etc/systemd/resolved.conf.d/dns.conf << 'EOF'
[Resolve]
DNS=94.140.14.14#dns.adguard-dns.com 94.140.15.15#dns.adguard-dns.com 2a10:50c0::ad1:ff#dns.adguard-dns.com 2a10:50c0::ad2:ff#dns.adguard-dns.com
FallbackDNS=9.9.9.9#dns.quad9.net 149.112.112.112#dns.quad9.net 2620:fe::fe#dns.quad9.net 2620:fe::9#dns.quad9.net
DNSOverTLS=yes
DNSSEC=yes
Domains=~.
MulticastDNS=no
LLMNR=no
StaleRetentionSec=86400
EOF

Settings:

  • DNS=: primary DNS servers; format IP#hostname enables certificate validation for DNS-over-TLS
  • FallbackDNS=: backup servers used if all primary servers are unreachable
  • DNSOverTLS=yes: enforces encrypted DNS transport; fails if the server does not support TLS
  • DNSSEC=yes: validates DNSSEC signatures; rejects responses that fail validation
  • Domains=~.: routes all DNS queries through the configured servers, overriding any DHCP-provided DNS
  • MulticastDNS=no / LLMNR=no: disables mDNS and Link-Local Multicast Name Resolution
  • StaleRetentionSec=86400: serves cached records for up to 24 hours if upstream is unreachable

8.6 Time Synchronization (chrony with NTS)

Replace the contents of /etc/chrony.conf with:

cat > /etc/chrony.conf << 'EOF'
# NTS servers
server gps.ntp.br iburst nts
server a.st1.ntp.br iburst nts
server b.st1.ntp.br iburst nts
server time.cloudflare.com iburst nts

# Authentication policy
authselectmode require
minsources 2

# Clock correction
makestep 1.0 3
leapseclist /usr/share/zoneinfo/leap-seconds.list

# Drift and state files
driftfile /var/lib/chrony/drift
dumpdir /var/lib/chrony
ntsdumpdir /var/lib/chrony

# RTC tracking
rtcfile /var/lib/chrony/rtc
rtcautotrim 30
rtconutc

# Disable remote command port
cmdport 0
EOF

Settings:

  • server ... nts: NTS-capable NTP servers with authenticated, encrypted time synchronization
  • iburst: sends a burst of packets on startup for faster initial synchronization
  • authselectmode require: enforces that all sources use NTS; unauthenticated sources are automatically excluded from selection
  • minsources 2: requires at least two sources to agree before updating the clock
  • makestep 1.0 3: allows a step correction in the first three updates to handle large initial clock errors (e.g., laptop booting after extended powered-off period)
  • ntsdumpdir: saves NTS session keys/cookies between restarts, avoiding a full handshake on each boot
  • rtcfile / rtcautotrim / rtconutc: tracks RTC drift and keeps it synchronized, assuming RTC keeps UTC time
  • cmdport 0: disables the UDP command port; local chronyc access still works via Unix socket

Note: Remove rtconutc when dual-booting with Windows, which sets the RTC to local time.

Create the chronyd arguments file:

mkdir -p /etc/sysconfig
echo "OPTIONS='-r -s'" > /etc/sysconfig/chronyd

-r: restores saved state from dumpdir on startup; -s: steps the clock from the RTC on startup.

8.7 Journal Size Limits

mkdir -p /etc/systemd/journald.conf.d
cat > /etc/systemd/journald.conf.d/size.conf << 'EOF'
[Journal]
SystemMaxUse=500M
SystemKeepFree=1G
RuntimeMaxUse=100M
MaxFileSec=1month
EOF

8.8 Configure resolv.conf

ln -sf /run/systemd/resolve/stub-resolv.conf /etc/resolv.conf

The target path does not need to exist at creation time; it will be present once systemd-resolved starts after the first boot.

8.9 Firewall Configuration

The default /etc/nftables.conf shipped with the nftables package is already a stateful drop-by-default ruleset. No modification is required.

Verify the content reflects a drop policy on the input chain:

cat /etc/nftables.conf

Note: If SSH is enabled (see Section 14.9), add tcp dport ssh accept before the rate-limit line in the input chain.


9. Configure Boot

9.1 Create UKI Directory

mkdir -p /efi/EFI/Linux

9.2 Configure UKI Preset

Edit the preset file:

vim /etc/mkinitcpio.d/linux-hardened.preset

Set the following:

ALL_kver="/boot/vmlinuz-linux-hardened"

PRESETS=('default')

default_uki="/efi/EFI/Linux/arch-linux-hardened.efi"

Note: The absence of ALL_config enables drop-in configuration files from /etc/mkinitcpio.conf.d/ (required for mkinitcpio ≥ 36).

9.3 Configure mkinitcpio

vim /etc/mkinitcpio.conf

Set:

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

MODULES:

  • tpm_crb: required before sd-encrypt runs so that the TPM 2.0 device is available for automatic unlock during early boot
  • amdgpu: ensures the display is active during the LUKS password prompt via early KMS; kms hook uses autodetect but explicit listing guarantees inclusion

HOOKS notes:

  • modconf includes all files from /etc/modprobe.d/ into the initramfs — the module blacklists and options configured in Part 2 take effect from the earliest boot stage
  • The btrfs hook is intentionally absent; it is only required for multi-device Btrfs configurations. The filesystems hook is sufficient for a single-device Btrfs root
  • sd-vconsole replaces the legacy keymap and consolefont hooks; do not use both
  • Hook order is significant; do not rearrange without understanding dependencies

If not using TPM: omit tpm_crb from MODULES.

If using a different GPU: replace amdgpu with the appropriate module (e.g., i915 for Intel).

9.4 Get UUIDs

ROOT_PART_UUID=$(blkid -s UUID -o value ${ROOT_DISK}p2)
DATA_PART_UUID=$(blkid -s UUID -o value ${DATA_DISK}1)
ROOT_FS_UUID=$(blkid -s UUID -o value /dev/mapper/cryptroot)

9.5 Create Kernel Command Line

cat > /etc/kernel/cmdline << EOF
rd.luks.name=${ROOT_PART_UUID}=cryptroot rd.luks.name=${DATA_PART_UUID}=cryptdata root=UUID=${ROOT_FS_UUID} rootflags=subvol=@ rw lsm=landlock,lockdown,yama,integrity,apparmor,bpf lockdown=integrity amd_pstate=active iommu=pt sysrq_always_enabled=1 zswap.enabled=0 nowatchdog
EOF

See Appendix C for parameter explanations.

9.6 Create Crypttab

cat > /etc/crypttab.initramfs << EOF
cryptroot   UUID=${ROOT_PART_UUID}   none   discard,timeout=120s,tries=5,password-echo=no,no-read-workqueue,no-write-workqueue,x-initrd.attach

cryptdata   UUID=${DATA_PART_UUID}   none   discard,timeout=120s,tries=5,password-echo=no,no-read-workqueue,no-write-workqueue
EOF

Key options:

  • discard: passes TRIM requests through the dm-crypt layer to the underlying SSD; required for Btrfs async discard to function through the encryption boundary (see Appendix A.3)
  • no-read-workqueue / no-write-workqueue: bypass the dm-crypt internal workqueue, processing requests synchronously; reduces latency on SSDs where queue overhead exceeds benefit (requires kernel ≥ 5.9)
  • timeout=120s: password entry timeout (0 waits indefinitely)
  • tries=5: maximum password attempts
  • password-echo=no: suppresses all password feedback; masked (the default) echoes asterisks
  • x-initrd.attach: keeps the encrypted root device open during shutdown; prevents systemd from attempting to detach it while still in use; not required for cryptdata

Security note: discard leaks information about which storage blocks contain data. For high-security threat models, remove discard from both entries and rely on periodic fstrim alone.

See Appendix C.2 for all options.

9.7 Generate Initramfs

mkinitcpio -P

This creates /efi/EFI/Linux/arch-linux-hardened.efi.

9.8 Create EFISTUB Boot Entry

efibootmgr -d ${ROOT_DISK} -p 1 -c -L "Arch Linux" -l '\EFI\Linux\arch-linux-hardened.efi' -v

The UEFI firmware loads and boots the UKI .efi file directly via the kernel's built-in EFI stub, without an intermediate bootloader.


10. Secure Boot Setup (Optional)

10.1 Check Status

sbctl status

10.2 Create and Enroll Keys

sbctl create-keys
sbctl enroll-keys -m -f
  • -m: includes Microsoft UEFI certificates for hardware firmware compatibility
  • -f: includes OEM firmware certificates to preserve firmware update capability

Note: -f may fail on systems without OEM certificates. Remove it and retry if that occurs.

10.3 Sign UKI

sbctl sign -s /efi/EFI/Linux/arch-linux-hardened.efi

The -s flag registers the path in sbctl's database. The included pacman hook uses this database to automatically re-sign updated UKIs on every kernel update, maintaining Secure Boot without manual intervention.

10.4 Verify

sbctl verify

11. Reboot

Exit chroot:

exit

Sync and reboot:

sync
systemctl reboot --firmware-setup

Before booting:

  1. If using Secure Boot: enable it in firmware settings
  2. Remove installation media
  3. Set boot order to prioritize the "Arch Linux" entry

First boot: enter the LUKS passphrase when prompted. Verify network connectivity after login: ping archlinux.org.


12. TPM Enrollment (Optional)

TPM enrollment configures the TPM 2.0 chip to automatically provide the LUKS unlock key when the system boots with verified firmware and a signed kernel. Manual passphrase entry is still required as a fallback.

Prerequisites: Secure Boot must be fully configured and active before TPM enrollment. PCR 7 (Secure Boot state) is used as part of the unlock policy.

12.1 Export Volume Paths

export ROOT_DISK="/dev/nvme0n1"
export DATA_DISK="/dev/sda"
export ROOT_PART_UUID=$(blkid -s UUID -o value ${ROOT_DISK}p2)
export DATA_PART_UUID=$(blkid -s UUID -o value ${DATA_DISK}1)

12.2 Create Recovery Keys

These are fallback keys stored in separate LUKS keyslots. They allow access if the passphrase is forgotten or the TPM fails.

systemd-cryptenroll --recovery-key ${ROOT_DISK}p2
systemd-cryptenroll --recovery-key ${DATA_DISK}1

Store the displayed recovery keys on offline physical media. Without them, TPM failure combined with a forgotten passphrase means permanent data loss.

12.3 Enroll TPM

Enroll both volumes using PCR 7 (Secure Boot policy) and PCR 15 (volume key measurement):

systemd-cryptenroll \
  --tpm2-device=auto \
  --tpm2-pcrs=7+15:sha256=0000000000000000000000000000000000000000000000000000000000000000 \
  --tpm2-with-pin=yes \
  ${ROOT_DISK}p2
systemd-cryptenroll \
  --tpm2-device=auto \
  --tpm2-pcrs=7+15:sha256=0000000000000000000000000000000000000000000000000000000000000000 \
  --tpm2-with-pin=yes \
  ${DATA_DISK}1

PCR policy explanation:

  • PCR 7 binds the key to the current Secure Boot state. If Secure Boot is disabled or keys are changed, the TPM will refuse to unlock.
  • PCR 15 starts at all zeros and is extended by systemd-cryptsetup with a hash of the volume key immediately after unlock. The all-zero prediction means: unlock only if PCR 15 has not yet been extended — i.e., only during the first (legitimate) boot unlock. This prevents replay attacks.
  • --tpm2-with-pin=yes requires a PIN in addition to the correct PCR state; the TPM-PIN combination forms a two-factor unlock. The PIN is separate from the LUKS passphrase.

If not using Secure Boot, use PCR 15 alone:

systemd-cryptenroll \
  --tpm2-device=auto \
  --tpm2-pcrs=15:sha256=0000000000000000000000000000000000000000000000000000000000000000 \
  --tpm2-with-pin=yes \
  ${ROOT_DISK}p2

12.4 Update Crypttab

Edit /etc/crypttab.initramfs and add TPM options to both entries:

cryptroot  UUID=<ROOT_PART_UUID>  none  discard,timeout=120s,tries=5,password-echo=no,no-read-workqueue,no-write-workqueue,x-initrd.attach,tpm2-device=auto,tpm2-measure-pcr=yes

cryptdata  UUID=<DATA_PART_UUID>  none  discard,timeout=120s,tries=5,password-echo=no,no-read-workqueue,no-write-workqueue,tpm2-device=auto,tpm2-measure-pcr=yes

New options:

  • tpm2-device=auto: automatically discovers the TPM 2.0 device; specify a path (e.g., /dev/tpmrm0) only when multiple TPMs are present
  • tpm2-measure-pcr=yes: extends PCR 15 with a hash of the volume key after successful unlock, establishing the PCR 15 policy used at enrollment

12.5 Back Up Updated LUKS Headers

TPM enrollment adds a new keyslot to the LUKS header. The header has changed since the initial backup in Section 3.4. Create updated backups:

cryptsetup luksHeaderBackup ${ROOT_DISK}p2 --header-backup-file cryptroot-header-post-tpm.img
cryptsetup luksHeaderBackup ${DATA_DISK}1  --header-backup-file cryptdata-header-post-tpm.img

12.6 Regenerate Initramfs and Re-sign

Renegerating the initramfs will now automatically hook Secure Boot resigning with sbctl

mkinitcpio -P

12.7 Reboot and Test

systemctl reboot

The system should prompt for the TPM PIN and then unlock automatically. If the PIN is unknown or TPM unlock fails, the system falls back to the passphrase.

12.8 Fixate Volume Key (Optional)

After a successful TPM-unlocked boot, extract the volume key hashes from the TPM measurement log:

grep 'volume-key' /run/log/systemd/tpm2-measure.log | grep -o '[0-9a-f]\{64\}'

This outputs two SHA-256 digests on separate lines: the first corresponds to cryptroot, the second to cryptdata, in unlock order.

Add fixate-volume-key=<digest> to the corresponding entry in /etc/crypttab.initramfs:

cryptroot  UUID=<ROOT_PART_UUID>  none  ...,fixate-volume-key=<cryptroot-sha256-digest>
cryptdata  UUID=<DATA_PART_UUID>  none  ...,fixate-volume-key=<cryptdata-sha256-digest>

This pins the expected volume key hash. If a rogue operating system attempts to impersonate the root partition using copied partition UUIDs and metadata, it will have a different volume key and unlock will be refused. Requires systemd ≥ 260.

Note: If the volume is ever re-encrypted with cryptsetup reencrypt, the volume key changes. The crypttab entries must be updated with the new hashes.

Regenerate the initramfs after updating crypttab:

mkinitcpio -P

13. Post-Installation Setup

13.1 Install Additional Packages

pacman -S mesa vulkan-radeon libva-mesa-driver tlp \
    pipewire pipewire-pulse pipewire-alsa pipewire-jack wireplumber \
    xdg-desktop-portal xdg-desktop-portal-wlr xdg-desktop-portal-gtk

Package variations:

  • Intel GPU: replace vulkan-radeon with vulkan-intel, replace libva-mesa-driver with intel-media-driver
  • libva-mesa-driver: VA-API hardware video decoding for AMD GPUs (MPEG-2, H.264, H.265, AV1 depending on GPU generation)

13.2 Enable TLP service

systemctl enable tlp
systemctl mask systemd-rfkill.service systemd-rfkill.socket

TLP manages radio device power state. systemd-rfkill must be masked to prevent conflicts.

13.3 Configure Zram

mkdir -p /etc/systemd/zram-generator.conf.d
cat > /etc/systemd/zram-generator.conf.d/zram.conf << 'EOF'
[zram0]
compression-algorithm = zstd
zram-size = min(ram / 2)
EOF

compression-algorithm = zstd is the current kernel default and is set explicitly to ensure consistent behavior if the default changes in a future kernel. The zram-generator default caps the device at 4096 MB regardless of available RAM — appropriate for low-memory systems but too conservative for systems with 16 GB or more. min(ram / 2) with a single argument removes the cap, creating a device of half the installed RAM unconditionally. The kernel documentation notes values up to twice physical RAM are reasonable given typical compression ratios; half is a conservative and widely-used baseline.

Advanced compression: For systems where minimizing swap latency is critical, a two-tier algorithm can be used:

compression-algorithm = lzo-rle zstd(level=3) (type=idle)

lzo-rle is the primary algorithm for active pages — faster compression and decompression, lower latency under pressure. zstd(level=3) is the recompression algorithm applied to idle pages in the background, achieving a better compression ratio at higher CPU cost. (type=idle) is a global recompression parameter that restricts recompression to idle pages only; active pages are never recompressed. Requires kernel ≥ 6.2 with CONFIG_ZRAM_MULTI_COMP.

13.4 Enable Periodic TRIM

systemctl enable fstrim.timer

Btrfs async discard (kernel default) continuously sends TRIM commands for freed blocks through the dm-crypt discard gate. The weekly fstrim sweep complements this by catching any missed blocks and handling the ESP, which has no async discard mechanism. See Appendix A.3 for a full explanation of how these mechanisms interact.

13.5 AppArmor Profile Cache

Enabling profile caching significantly reduces boot time by avoiding recompilation of profiles on each start:

vim /etc/apparmor/parser.conf

Uncomment:

write-cache

After the next boot, verify profiles loaded correctly:

sudo aa-status

Expected output: 100+ profiles loaded, the majority in enforce mode.


Part 2: System Hardening and Optimization

14. Security Hardening

14.1 Core Security Sysctl

cat > /etc/sysctl.d/99-security.conf << 'EOF'
# Kernel hardening
kernel.kptr_restrict = 2
kernel.perf_event_paranoid = 3
kernel.kexec_load_disabled = 1
kernel.unprivileged_userfaultfd = 0
kernel.panic = 10
kernel.oops_limit = 100
kernel.warn_limit = 100
kernel.printk = 3 3 3 3
dev.tty.ldisc_autoload = 0

# Filesystem hardening
fs.suid_dumpable = 0
fs.protected_regular = 2
fs.protected_fifos = 2

# Network security
net.ipv4.tcp_rfc1337 = 1
net.ipv4.conf.all.rp_filter = 1
net.ipv4.conf.default.rp_filter = 1
net.ipv4.conf.all.accept_redirects = 0
net.ipv4.conf.default.accept_redirects = 0
net.ipv4.conf.all.secure_redirects = 0
net.ipv4.conf.default.secure_redirects = 0
net.ipv4.conf.all.send_redirects = 0
net.ipv4.conf.default.send_redirects = 0
net.ipv4.conf.all.accept_source_route = 0
net.ipv4.conf.default.accept_source_route = 0
net.ipv4.conf.all.log_martians = 1
net.ipv4.conf.default.log_martians = 1
net.ipv4.icmp_echo_ignore_broadcasts = 1
net.ipv4.icmp_ignore_bogus_error_responses = 1
net.ipv6.conf.all.accept_redirects = 0
net.ipv6.conf.default.accept_redirects = 0
net.ipv6.conf.all.accept_source_route = 0
net.ipv6.conf.default.accept_source_route = 0
net.ipv6.conf.all.use_tempaddr = 2
net.ipv6.conf.default.use_tempaddr = 2
net.ipv4.tcp_timestamps = 0
EOF

Settings reference: Appendix D.1. For settings considered but intentionally not applied, see Appendix D.3.

14.2 Linux-Hardened Specific Settings

The linux-hardened kernel sets kexec_load_disabled and unprivileged_userfaultfd to secure values by default via kernel config. The entries above make those defaults explicit and portable. The following setting is linux-hardened specific and does not exist in mainline kernels:

cat > /etc/sysctl.d/99-linux-hardened.conf << 'EOF'
# linux-hardened disables unprivileged user namespaces by default.
# Required for: Steam sandbox, bubblewrap, Flatpak, browser renderer isolation.
kernel.unprivileged_userns_clone = 1
EOF

Note: This sysctl key does not exist on mainline or zen kernels. If switching kernels, remove this file.

14.3 Disable Unused Modules

cat > /etc/modprobe.d/99-blacklist.conf << 'EOF'
install sp5100_tco /bin/false
install wdat_wdt /bin/false
install pcspkr /bin/false
EOF

Using install module /bin/false rather than blacklist module prevents even explicit modprobe invocation, not just autoloading. The modconf mkinitcpio hook includes this file in the initramfs, so these restrictions take effect from the first module-loading phase.

14.4 Disable Unused Network Protocols

cat > /etc/modprobe.d/network-protocols.conf << 'EOF'
install dccp /bin/false
install sctp /bin/false
install rds /bin/false
install tipc /bin/false
install n-hdlc /bin/false
install ax25 /bin/false
install netrom /bin/false
install x25 /bin/false
install rose /bin/false
install decnet /bin/false
install econet /bin/false
install af_802154 /bin/false
install ipx /bin/false
install appletalk /bin/false
install psnap /bin/false
install p8023 /bin/false
install p8022 /bin/false
install can /bin/false
install atm /bin/false
EOF

14.5 Disable Unused Filesystems and Hardware

cat > /etc/modprobe.d/unused-modules.conf << 'EOF'
# Unused filesystems
install cramfs /bin/false
install freevxfs /bin/false
install jffs2 /bin/false
install gfs2 /bin/false
install reiserfs /bin/false
install jfs /bin/false
install minix /bin/false
install sysv /bin/false

# FireWire (DMA attack vector)
install firewire-core /bin/false
install firewire-ohci /bin/false
install firewire_sbp2 /bin/false
install firewire-net /bin/false
install ohci1394 /bin/false
install sbp2 /bin/false
install dv1394 /bin/false
install raw1394 /bin/false
install video1394 /bin/false

# Test and GNSS drivers
install vivid /bin/false
install gnss /bin/false
install gnss-mtk /bin/false
install gnss-serial /bin/false
install gnss-sirf /bin/false
install gnss-usb /bin/false
install gnss-ubx /bin/false

# Legacy hardware
install floppy /bin/false
install parport /bin/false
install parport_pc /bin/false
install pcmcia /bin/false
install yenta_socket /bin/false
EOF

14.6 Disable Legacy Framebuffer Drivers

cat > /etc/modprobe.d/framebuffer.conf << 'EOF'
blacklist cyber2000fb
blacklist cyblafb
blacklist gx1fb
blacklist hgafb
blacklist lxfb
blacklist matroxfb_base
blacklist neofb
blacklist pm2fb
blacklist s1d13xxxfb
blacklist sisfb
blacklist tdfxfb
blacklist tridentfb
blacklist udlfb
blacklist vfb
blacklist vesafb
blacklist vt8623fb
EOF

14.7 Regenerate Initramfs

The modconf hook must pick up the new modprobe.d files:

mkinitcpio -P

14.8 Default umask (Optional)

Changing the system-wide default umask to 027 restricts new file permissions so that group members have read/execute access but no world access:

vim /etc/login.defs

Set:

UMASK 027

Note: A umask of 027 can break some applications that expect world-readable files in shared paths. Test on a non-critical system before applying broadly. The system default of 022 is appropriate if any issues arise.

14.9 SSH Configuration (Optional)

Only enable sshd if remote access is required. Enabling it on a system not intended for remote access increases attack surface without benefit.

Harden the SSH daemon configuration:

vim /etc/ssh/sshd_config
PermitRootLogin no
PasswordAuthentication no
AuthenticationMethods publickey
X11Forwarding no
AllowTcpForwarding no
PrintMotd no

Add an SSH accept rule to nftables before the rate-limit line in the input chain:

vim /etc/nftables.conf

Add before pkttype host limit rate:

tcp dport ssh accept comment "allow SSH"

Enable:

systemctl enable --now sshd

Generate and deploy keys from the client machine:

ssh-keygen -t ed25519 -C "user@hostname"
ssh-copy-id -i ~/.ssh/id_ed25519.pub username@server

15. Performance Tuning

15.1 TCP and Network Performance

cat > /etc/sysctl.d/99-performance.conf << 'EOF'
# TCP improvements
net.ipv4.tcp_fastopen = 3
net.ipv4.tcp_mtu_probing = 1
net.ipv4.tcp_fin_timeout = 30
net.ipv4.tcp_slow_start_after_idle = 0
net.ipv4.tcp_congestion_control = bbr
net.ipv4.ip_local_port_range = 30000 65535
net.core.netdev_max_backlog = 4096

# Filesystem
fs.inotify.max_user_instances = 8192

# Memory management
vm.compaction_proactiveness = 0
vm.min_free_kbytes = 614400
vm.vfs_cache_pressure = 40
vm.page_lock_unfairness = 1

# Memory writeback
vm.dirty_background_bytes = 268435456
vm.dirty_bytes = 536870912
vm.dirty_writeback_centisecs = 1000

# Applications requiring large memory maps (games)
vm.max_map_count = 2147483642
EOF

Settings reference: Appendix D.4.

15.2 BBR Module

Ensure tcp_bbr loads at boot:

echo "tcp_bbr" > /etc/modules-load.d/tcp_bbr.conf

15.3 Zram Sysctl

cat > /etc/sysctl.d/99-vm-zram.conf << 'EOF'
# Zram-optimized swap parameters
vm.swappiness = 180
vm.page-cluster = 0
vm.watermark_scale_factor = 125
vm.watermark_boost_factor = 0
EOF

Settings reference: Appendix D.5.


16. Hardware Optimization

I/O scheduler configuration, SATA power management, CPU/DMA latency tuning, and audio device power rules are covered in the companion hardware optimization guide.

Reference values used on this system:

  • NVMe scheduler: kyber
  • SATA SSD scheduler: mq-deadline
  • SATA link power: med_power_with_dipm

17. Audio Configuration

17.1 PipeWire Services

PipeWire and its session manager run as user services:

systemctl --user enable pipewire pipewire-pulse wireplumber

17.2 Real-Time Privileges

PipeWire uses RealtimeKit (rtkit) for real-time scheduling and does not require manual group-based rtprio limits for normal operation. The following limits support direct hardware access in ALSA/OSS remote sessions and non-PipeWire audio applications:

cat > /etc/security/limits.d/20-audio.conf << 'EOF'
@audio - rtprio 99
@audio - memlock unlimited
EOF

Add the user to the audio group:

usermod -aG audio username

18. Power Management

18.1 TLP Configuration

TLP optimizes power consumption automatically. Override default behavior with a drop-in configuration:

cat > /etc/tlp.d/00-custom.conf << 'EOF'
DEVICES_TO_ENABLE_ON_STARTUP="wifi bluetooth"
USB_AUTOSUSPEND=0
EOF

DEVICES_TO_ENABLE_ON_STARTUP: forces listed devices on at startup regardless of their state at last shutdown. Remove a device from this list to preserve its last-used state across reboots.

USB_AUTOSUSPEND=0: disables USB autosuspend for all USB devices. Enable selectively if specific devices behave correctly with power saving.


19. Optional Enhancements

19.1 Btrfs Snapshots with Snapper

Install Snapper and the optional PAC hook for automatic pre/post snapshots on pacman operations:

pacman -S snapper snap-pac

Create a Snapper configuration for the root subvolume:

snapper -c root create-config /

Verify the configuration:

snapper list-configs

Enable automatic snapshot cleanup:

systemctl enable snapper-cleanup.timer

For snapshot browsing and rollback, see the Arch Wiki: Snapper.

19.2 Paccache Hook

Automatically prune the pacman cache on each transaction, retaining the three most recent versions:

pacman -S pacman-contrib
cat > /etc/pacman.d/hooks/paccache.hook << 'EOF'
[Trigger]
Operation = Upgrade
Operation = Install
Operation = Remove
Type = Package
Target = *

[Action]
Description = Cleaning pacman cache...
When = PostTransaction
Exec = /usr/bin/paccache -r
EOF

19.3 NT Synchronization (Wine/Proton)

ntsync provides kernel-level NT synchronization primitive emulation, significantly improving Wine and Proton performance by reducing syscall overhead for Windows synchronization objects.

echo "ntsync" > /etc/modules-load.d/ntsync.conf

Verify the module loaded after reboot:

lsmod | grep ntsync

19.4 Efivarfs Read-Only Protection (Optional)

Mounting efivarfs read-only prevents OS-level exploitation of UEFI variables (LogoFAIL and related vulnerabilities). efivarfs (/sys/firmware/efi/efivars) exposes UEFI runtime variables — boot order, Secure Boot key databases, firmware flags. This is entirely separate from the ESP filesystem (/efi): the ESP is a FAT32 partition on disk; efivarfs is a virtual filesystem exposing firmware state through the kernel.

Add to /etc/fstab:

efivarfs    /sys/firmware/efi/efivars    efivarfs    ro,nosuid,nodev,noexec    0 0

Kernel updates do not require a remount. The sbctl pacman hook signs EFI binary files on the ESP filesystem, which is an ordinary disk write and is unaffected by efivarfs being read-only.

A temporary remount is only needed for operations that write UEFI runtime variables directly: efibootmgr (adding or modifying boot entries), sbctl enroll-keys (writing Secure Boot keys to firmware), or systemctl reboot --firmware-setup (setting the OsIndications variable).

mount -o remount,rw /sys/firmware/efi/efivars

19.5 KVM and Virtualization

KVM configuration and virtual machine setup are covered in the companion virtualization guide.


Appendices


Appendix A: Btrfs Reference

A.1 Subvolume Layout

cryptroot (LUKS)
└── Btrfs filesystem (LABEL=ROOT)
    ├── @           → /
    ├── @home       → /home
    ├── @var        → /var
    └── @snapshots  → /snapshots

cryptdata (LUKS)
└── Btrfs filesystem (LABEL=DATA)
    └── /           → /data

The flat subvolume layout places all subvolumes directly under the Btrfs root rather than nested under @. This simplifies snapshot management because rolling back @ does not affect @home or @var, and vice versa.

@var is a separate subvolume because it contains frequently-changing data (logs, databases, cache) that should not be included in system snapshots. Including /var in a snapshot and then rolling back to it can create inconsistency between the filesystem state and running service state.

@snapshots is a separate subvolume so that snapshots themselves are not included in snapshots (which would create recursive snapshot behavior).

A.2 Mount Option Inheritance

Btrfs mount options apply at the filesystem level from the first mount. compress=zstd:2 set on @ applies to all subvolumes on that device — specifying it on subsequent subvolume mounts in fstab is redundant but harmless. noatime is a VFS-level option and must be specified per-mount.

A.3 TRIM and Async Discard

Three mechanisms handle TRIM on this setup, working as a layered stack:

discard in crypttab opens the TRIM passthrough gate in dm-crypt. TRIM commands issued by the filesystem travel through the encryption layer to the physical device. Without this option, all TRIM commands are silently dropped at the dm-crypt boundary regardless of what the filesystem does.

Btrfs async discard (discard=async, kernel default since 6.2) is Btrfs's built-in mechanism. When blocks are freed, Btrfs batches them and sends TRIM commands asynchronously in the background. This avoids the write-latency penalty of synchronous discard (which would block until the SSD acknowledges each TRIM).

fstrim.timer runs fstrim weekly, requesting that the filesystem report all currently-free blocks to the device in a single pass. This catches anything async discard may have missed and handles the ESP (vfat has no async discard).

The dependency: discard in crypttab must be present for either async discard or fstrim to reach the physical device. Without it, both mechanisms generate TRIM commands that are immediately discarded by dm-crypt.

Security consideration: discard leaks information about which storage blocks contain data — an observer with physical access can determine the approximate size and location of encrypted data. For high-security deployments, remove discard from crypttab and rely on fstrim alone, or disable TRIM entirely.


Appendix B: Package Purposes

Core System

Package Purpose
base Minimal base system utilities
base-devel Build tools required by AUR and some packages
linux-hardened Security-hardened kernel; enhanced ASLR, restrictive compile-time defaults, additional patch set. Alternative: linux-zen (performance-oriented, no hardening)
linux-hardened-headers Kernel headers; required for DKMS module compilation
linux-firmware Firmware blobs for hardware peripherals
amd-ucode AMD CPU microcode updates loaded early by the initramfs
sof-firmware Sound Open Firmware; primarily for Intel SOF-based audio hardware; harmless on AMD

Storage and Encryption

Package Purpose
cryptsetup LUKS/dm-crypt management
btrfs-progs Btrfs userspace tools
zram-generator Systemd-native zram device configuration

Boot

Package Purpose
efibootmgr Creates and manages UEFI boot entries
sbctl Secure Boot key management and UKI signing
tpm2-tools TPM 2.0 command-line utilities
tpm2-tss TPM 2.0 Software Stack; required for systemd-cryptenroll TPM operations

Networking

Package Purpose
iwd Intel Wireless Daemon; Wi-Fi management with built-in MAC randomization
systemd-resolvconf Provides resolvconf compatibility shim; ensures tools that write to resolv.conf work with systemd-resolved
chrony NTP client/server with NTS (authenticated, encrypted time sync) support
wireless-regdb Regulatory domain database for the wireless subsystem
iptables-nft iptables compatibility layer over nftables backend; required by tools that use iptables directly
nftables Netfilter packet filtering framework

Security

Package Purpose
apparmor Mandatory access control; profiles restrict what individual programs can access

Graphics

Package Purpose
mesa OpenGL and Vulkan stack for AMD/Intel (open-source)
vulkan-radeon AMD Vulkan driver (ACO/RADV)
libva-mesa-driver VA-API hardware video decoding for AMD GPUs

Audio

Package Purpose
pipewire Low-latency audio/video router and processor
pipewire-pulse PulseAudio compatibility layer over PipeWire
pipewire-alsa ALSA compatibility layer over PipeWire
pipewire-jack JACK compatibility layer over PipeWire
wireplumber PipeWire session/policy manager

Power

Package Purpose
tlp Advanced power management; optimizes CPU, PCI, SATA, and USB power states

Portals and Integration

Package Purpose
flatpak Application sandboxing and distribution
xdg-desktop-portal Desktop portal interface for sandboxed app integration
xdg-desktop-portal-gtk GTK backend for desktop portal

Appendix C: Boot Configuration Reference

C.1 Kernel Command Line Parameters

Parameter Value Purpose
rd.luks.name UUID=name Maps LUKS UUID to device name in initramfs
root UUID=... Root filesystem by UUID
rootflags subvol=@ Btrfs subvolume to mount as root
rw Mounts root read-write; required by the fsck hook
lsm landlock,lockdown,yama,integrity,apparmor,bpf Loads Linux Security Modules in specified order; order matters for stacking
lockdown integrity Activates the lockdown LSM in integrity mode — prevents userland from modifying the running kernel (loading unsigned modules, writing to /dev/mem, etc.). Not activated automatically by Secure Boot.
amd_pstate active Enables AMD P-State driver in active mode; CPU firmware autonomously manages frequencies using EPP hints
iommu pt IOMMU passthrough mode; devices that do not require DMA isolation bypass the IOMMU, reducing overhead without weakening isolation for devices that do
sysrq_always_enabled 1 Keeps SysRq permanently available for emergency recovery regardless of the runtime sysctl value
zswap.enabled 0 Disables zswap; prevents it competing with zram (linux-hardened may enable zswap by default via CONFIG_ZSWAP_DEFAULT_ON)
nowatchdog Disables both the soft-lockup detector and the NMI watchdog; reduces timer interrupts

C.2 Crypttab Options

Option Purpose
discard Passes TRIM commands through dm-crypt to the physical device; required for Btrfs async discard to function through the encryption layer. Security implication: leaks which blocks contain data.
timeout=120s Password entry timeout; 0 waits indefinitely
tries=5 Maximum failed passphrase attempts before giving up
password-echo=no Suppresses all password feedback; masked (default) echoes asterisks
no-read-workqueue Bypasses dm-crypt internal read workqueue for synchronous processing; reduces latency on fast devices (requires kernel ≥ 5.9)
no-write-workqueue Same as above for writes
x-initrd.attach Keeps the device open during shutdown; prevents systemd from detaching the root device while still in use. Only needed on the root device.
tpm2-device=auto Enables TPM 2.0 unlock; auto discovers the device automatically. Specify a path only when multiple TPMs are present.
tpm2-measure-pcr=yes Extends PCR 15 with a hash of the volume key after unlock, establishing the PCR measurement used by the TPM enrollment policy
fixate-volume-key=<hash> Pins the expected volume key SHA-256 hash; refuses attachment if the hash does not match, preventing volume impersonation attacks. Requires systemd ≥ 260.
password-cache= Caches entered passphrases in the kernel keyring for 2.5 minutes; when multiple volumes share a passphrase, only one entry is required. Default: yes.

Appendix D: Sysctl Reference

D.1 Security Sysctl

Key Value Purpose
kernel.kptr_restrict 2 Hides kernel symbol addresses from all users including root
kernel.perf_event_paranoid 3 Disables performance events for unprivileged users
kernel.kexec_load_disabled 1 Prevents loading a new kernel via kexec (linux-hardened default)
kernel.unprivileged_userfaultfd 0 Disables userfaultfd for unprivileged users; closes a class of kernel exploit primitives (linux-hardened default)
kernel.panic 10 Reboots 10 seconds after a kernel panic
kernel.oops_limit 100 Limits kernel oops messages before rebooting
kernel.warn_limit 100 Limits kernel warning messages
kernel.printk 3 3 3 3 Suppresses kernel messages to console; kernel ring buffer still accessible via dmesg
dev.tty.ldisc_autoload 0 Prevents unprivileged loading of TTY line discipline modules
fs.suid_dumpable 0 Disables core dumps for setuid programs
fs.protected_regular 2 Prevents following hard links to regular files not owned by the follower
fs.protected_fifos 2 Prevents following hard links to FIFOs not owned by the follower
net.ipv4.tcp_rfc1337 1 Protects against TCP time-wait assassination
net.ipv4.conf.*.rp_filter 1 Strict reverse path filtering; drops packets arriving on unexpected interfaces
net.ipv4.conf.*.accept_redirects 0 Ignores ICMP redirect messages
net.ipv4.conf.*.secure_redirects 0 Ignores ICMP redirects even from listed gateways
net.ipv4.conf.*.send_redirects 0 Does not send ICMP redirects
net.ipv4.conf.*.accept_source_route 0 Ignores source-routed packets
net.ipv4.conf.*.log_martians 1 Logs packets with impossible source addresses
net.ipv4.icmp_echo_ignore_broadcasts 1 Ignores broadcast ICMP echo requests; prevents Smurf amplification
net.ipv4.icmp_ignore_bogus_error_responses 1 Ignores malformed ICMP error responses
net.ipv6.conf.*.accept_redirects 0 Ignores ICMPv6 redirect messages
net.ipv6.conf.*.accept_source_route 0 Ignores IPv6 source-routed packets
net.ipv6.conf.*.use_tempaddr 2 Enables IPv6 privacy extensions; uses temporary addresses for outgoing connections
net.ipv4.tcp_timestamps 0 Disables TCP timestamps; prevents remote uptime fingerprinting

D.2 Linux-Hardened Sysctl

Key Value Purpose
kernel.unprivileged_userns_clone 1 Re-enables unprivileged user namespaces; required for Steam, Flatpak, bubblewrap, and browser renderer sandboxes. linux-hardened disables this by default; this key does not exist on mainline kernels.

D.3 Settings Intentionally Not Applied

Key Reason
kernel.kexec_load_disabled = 1 linux-hardened default; listed in D.1 for explicitness and portability
kernel.unprivileged_userfaultfd = 0 linux-hardened default; listed in D.1 for explicitness
net.ipv4.tcp_syncookies = 1 Kernel default since 5.10
fs.protected_hardlinks = 1 Kernel default
fs.protected_symlinks = 1 Kernel default
fs.binfmt_misc.status = 0 Breaks Wine and Proton
net.core.default_qdisc = cake Resource overhead not justified for a laptop; BBR operates correctly without it
kernel.modules_disabled = 1 Breaks USB devices, VMs, and all runtime module loading
kernel.yama.ptrace_scope = 3 Breaks Wine, Proton, debuggers, and anti-cheat systems
net.ipv4.icmp_echo_ignore_all Breaks ping diagnostics; too disruptive without security benefit
kernel.sysrq = 0 Removes emergency recovery capability
hidepid Explicitly unsupported with systemd; breaks D-Bus, Polkit, Bluetooth, and other inter-process communication

D.4 Performance Sysctl

Key Value Purpose
net.ipv4.tcp_fastopen 3 Enables TCP Fast Open for both client and server connections; reduces connection setup latency
net.ipv4.tcp_mtu_probing 1 Enables MTU discovery; allows larger packets when ICMP blackhole is detected
net.ipv4.tcp_fin_timeout 30 Reduces TIME_WAIT socket timeout from 60 to 30 seconds
net.ipv4.tcp_slow_start_after_idle 0 Prevents TCP slow-start after an idle period on persistent connections
net.ipv4.tcp_congestion_control bbr Bottleneck Bandwidth and RTT congestion control; higher throughput and lower latency than CUBIC on most connections
net.ipv4.ip_local_port_range 30000 65535 Expands the ephemeral port range; beneficial for systems with many simultaneous connections
net.core.netdev_max_backlog 4096 Increases packet queue before the kernel drops packets on busy interfaces
fs.inotify.max_user_instances 8192 Increases inotify watch limit; required by development tools, file managers, and some games
vm.compaction_proactiveness 0 Disables proactive memory compaction; reduces background CPU usage on systems with sufficient RAM
vm.min_free_kbytes 614400 Increases minimum free memory reserve (~600MB); reduces allocation latency spikes
vm.vfs_cache_pressure 40 Reduces tendency to reclaim inode/dentry caches; improves filesystem performance with abundant RAM
vm.page_lock_unfairness 1 Reduces page lock contention in memory-intensive workloads
vm.dirty_background_bytes 268435456 Starts background writeback at 256MB dirty data
vm.dirty_bytes 536870912 Throttles processes at 512MB dirty data
vm.dirty_writeback_centisecs 1000 Writeback flush interval: 10 seconds
vm.max_map_count 2147483642 Maximum memory map count; required by games and some applications

D.5 Zram Sysctl

Key Value Purpose
vm.swappiness 180 Aggressively prefers zram over filesystem reclaim; values above 100 are valid for memory-compressed swap significantly faster than storage I/O
vm.page-cluster 0 Disables readahead for swap; appropriate for zram where sequential readahead has no benefit
vm.watermark_scale_factor 125 Increases the gap between low and high memory watermarks; reduces reclaim frequency
vm.watermark_boost_factor 0 Disables watermark boosting; prevents aggressive reclaim spikes after memory pressure events