Skip to content

Instantly share code, notes, and snippets.

@jensmeindertsma
Last active May 1, 2026 06:08
Show Gist options
  • Select an option

  • Save jensmeindertsma/b145741b446427285e19d16e0f834878 to your computer and use it in GitHub Desktop.

Select an option

Save jensmeindertsma/b145741b446427285e19d16e0f834878 to your computer and use it in GitHub Desktop.
Arch Linux LUKS BTRFS setup

We will be installing an Arch Linux system with full disk encryption, hibernation, a splash boot screen and snapshots.

Partitioning

First, we note the available block devices:

$ fdisk -l

Disk /dev/nvme1n1: 465.76 GiB, 500107862016 bytes, 976773168 sectors
Disk model: CT500P5PSSD8
Units: sectors of 1 * 512 = 512 bytes
Sector size (logical/physical): 512 bytes / 512 bytes
I/O size (minimum/optimal): 512 bytes / 512 bytes
Disklabel type: gpt
Disk identifier: 0731F441-2457-48D4-820D-DEF335B1329E


Disk /dev/nvme0n1: 1.82 TiB, 2000398934016 bytes, 3907029168 sectors
Disk model: CT2000P5PSSD8
Units: sectors of 1 * 512 = 512 bytes
Sector size (logical/physical): 512 bytes / 512 bytes
I/O size (minimum/optimal): 512 bytes / 512 bytes
Disklabel type: gpt
Disk identifier: 4741C1B6-FE54-4091-AA41-1A03090B417D

Device           Start        End    Sectors  Size Type
/dev/nvme0n1p1    2048    2099199    2097152    1G EFI System
/dev/nvme0n1p2 2099200    2131967      32768   16M Microsoft reserved
/dev/nvme0n1p3 2131968    6326271    4194304    2G Windows recovery environment
/dev/nvme0n1p4 6326272 3907028991 3900702720  1.8T Microsoft basic data

We can see that on this dual boot system, the /dev/nvme0n1 device has Windows installed on it. So we can use the /dev/nvme1n1 device to install Arch Linux. Now we will create the partitions:

$ fdisk /dev/nvme1n1

Welcome to fdisk (util-linux 2.41.3).
Changes will remain in memory only, until you decide to write them.
Be careful before using the write command.

# First we create the GPT partition table:

Command (m for help): g
Created a new GPT disklabel (GUID: DD52E254-2999-4A80-911F-2697A9782F15).

# Then we create a EFI System partition with a size of 1 GiB:

Command (m for help): n
Partition number (1-128, default 1):
First sector (2048-976773134, default 2048):
Last sector, +/-sectors or +/-size{K,M,G,T,P} (2048-976773134, default 976773119): +1G

Created a new partition 1 of type 'Linux filesystem' and of size 1 GiB.

Command (m for help): t
Selected partition 1
Partition type or alias (type L to list all): 1
Changed type of partition 'Linux filesystem' to 'EFI System'.

# Lastly we create a partition spanning the rest of the disk:

Command (m for help): n
Partition number (2-128, default 2):
First sector (2099200-976773134, default 2099200):
Last sector, +/-sectors or +/-size{K,M,G,T,P} (2099200-976773134, default 976773119):

Created a new partition 2 of type 'Linux filesystem' and of size 464.8 GiB.

# Let's check the table to make sure our partitions are in order:

Command (m for help): p
Disk /dev/nvme1n1: 465.76 GiB, 500107862016 bytes, 976773168 sectors
Disk model: CT500P5PSSD8
Units: sectors of 1 * 512 = 512 bytes
Sector size (logical/physical): 512 bytes / 512 bytes
I/O size (minimum/optimal): 512 bytes / 512 bytes
Disklabel type: gpt
Disk identifier: DD52E254-2999-4A80-911F-2697A9782F15

Device           Start       End   Sectors   Size Type
/dev/nvme1n1p1    2048   2099199   2097152     1G EFI System
/dev/nvme1n1p2 2099200 976773119 974673920 464.8G Linux filesystem

# After confirming this we can save the partition table:

Command (m for help): w
The partition table has been altered.
Calling ioctl() to re-read partition table.
Syncing disks.

Creating the filesystems

We will need two filesystems:

  • /dev/nvme1n1p1 is going to be our boot partition and so it needs a FAT32 filesystem:

    $ mkfs.fat -F 32 -n LINUX /dev/nvme1n1p1
      mkfs.fat 4.2 (2021-01-31)
    

    As you can see I've attached a filesystem label LINUX which will make it easier to refer to this filesystem when we describe its mountpoint in /etc/fstab later on.

  • /dev/nvme1n1p2 is going to be our root partition and we will first need to set up the disk encryption:

    # First we create a LUKS encryption volume:
    
    $ cryptsetup luksFormat /dev/nvme1n1p2
    
    WARNING!
    ========
    This will overwrite data on /dev/nvme1n1p2 irrevocably.
    
    Are you sure? (Type 'yes' in capital letters): YES
    Enter passphrase for /dev/nvme1n1p2:
    Verify passphrase:
    
    cryptsetup luksFormat /dev/nvme1n1p2  25.85s user 0.24s system 200% cpu 13.035 total
    
    # Next we unlock the volume and assign it the name "root":
    
    $ cryptsetup open /dev/nvme1n1p2 root
    Enter passphrase for /dev/nvme1n1p2:
    cryptsetup open /dev/nvme1n1p2 root  7.70s user 0.04s system 211% cpu 3.662 total
    
    # We can now see our new "root" volume:
    
    $ lsblk
    NAME        MAJ:MIN RM   SIZE RO TYPE
    nvme1n1     259:0    0 465.8G  0 disk
    ├─nvme1n1p1 259:1    0     1G  0 part
    └─nvme1n1p2 259:2    0 464.8G  0 part
      └─root    253:0    0 464.7G  0 crypt
    nvme0n1     259:3    0   1.8T  0 disk
    ├─nvme0n1p1 259:4    0     1G  0 part
    ├─nvme0n1p2 259:5    0    16M  0 part
    ├─nvme0n1p3 259:6    0     2G  0 part
    └─nvme0n1p4 259:7    0   1.8T  0 part
    

    Then finally we can create a Btrfs filesystem at /dev/mapper/root:

    $ mkfs.btrfs /dev/mapper/root
    btrfs-progs v6.19.1
    See https://btrfs.readthedocs.io for more information.
    
    NOTE: default settings have changed in version 6.19 (supported since linux 6.1):
          - enable block-group-tree (-O bgt)
    
    Label:              (null)
    UUID:               603bff8d-9c55-4172-b8cd-249f333416ba
    Node size:          16384
    Sector size:        4096	(CPU page size: 4096)
    Filesystem size:    464.75GiB
    Block group profiles:
      Data:             single            8.00MiB
      Metadata:         DUP               1.00GiB
      System:           DUP               8.00MiB
    SSD detected:       yes
    Zoned device:       no
    Features:           extref, skinny-metadata, no-holes, free-space-tree, block-group-tree
    Checksum:           crc32c
    Number of devices:  1
    Devices:
      ID        SIZE  PATH
        1   464.75GiB  /dev/mapper/root

Setting up Btrfs

We're using Btrfs so we can take snapshots of the whole system and easily roll-back to a previous snapshot when we screw up the system. But not all of the system files should be included in the snapshot. We can create Btrfs subvolumes and mount them to exclude those mountpoints from being included in the snapshot:

$ mount /dev/mapper/root /mnt
$ btrfs subvolume create /mnt/@
$ btrfs subvolume create /mnt/@home
$ btrfs subvolume create /mnt/@snapshots
$ btrfs subvolume create /mnt/@cache
$ btrfs subvolume create /mnt/@log
$ btrfs subvolume create /mnt/@swap
$ umount /mnt

As you can see we have one "root" subvolume we will mount at the root of our filesystem, and then additional subvolumes. When we take a snapshot of the @ subvolume, data inside any of the other subvolumes will not be included.

Next we will mount our subvolumes so we can install Linux onto the filesystem.

$ mount -o subvol=@ /dev/mapper/root /mnt

$ mkdir -p /mnt/home /mnt/.snapshots /mnt/var/cache /mnt/var/log /mnt/swap

$ mount -o subvol=@home /dev/mapper/root /mnt/home
$ mount -o subvol=@snapshots /dev/mapper/root /mnt/.snapshots
$ mount -o subvol=@cache /dev/mapper/root /mnt/var/cache
$ mount -o subvol=@log /dev/mapper/root /mnt/var/log
$ mount -o subvol=@swap /dev/mapper/root /mnt/swap

We also need to mount our boot partition:

$ mkdir -p /mnt/boot
$ mount -L LINUX /mnt/boot

To support hibernation, we will need to create a swapfile at least as large as our system memory. We need to use a special command for this because the swap file needs very specific attributes inside of a Btrfs filesystem:

$ btrfs filesystem mkswapfile --size 32G /mnt/swap/swapfile

create swapfile /mnt/swap/swapfile size 32.00GiB (34359738368)

$ swapon /mnt/swap/swapfile

System Installation

Now that we have our partitions and subvolumes all mounted, we can install a bare minimum Arch Linux installation onto them:

$ pacstrap -K /mnt base linux linux-firmware amd-ucode man-db man-pages texinfo efibootmgr refind sudo vim

Next we can fill out our filesystem table at /mnt/etc/fstab:

# Static information about the filesystems.
# See fstab(5) for details.

# <file system>		<dir>		<type>	<options>			<dump>	<pass>
/dev/mapper/root	/			btrfs	subvol=/@			0		0
/dev/mapper/root 	/home		btrfs	subvol=/@home		0		0
/dev/mapper/root 	/var/cache	btrfs	subvol=/@cache		0		0
/dev/mapper/root 	/var/log	btrfs	subvol=/@log		0		0
/dev/mapper/root	/swap		btrfs	subvol=/@swap		0		0
/dev/mapper/root	/.snapshots	btrfs	subvol=/@snapshots	0		0

LABEL=LINUX			/boot		vfat	umask=077			0		2

/swap/swapfile		none		swap	defaults			0		0

Before rooting into the system, we must not forget to specify the default subvolume to be mounted to /mnt at boot time:

$ btrfs subvolume list /mnt

ID 256 gen 48 top level 5 path @
ID 257 gen 9 top level 5 path @home
ID 258 gen 10 top level 5 path @snapshots
ID 259 gen 29 top level 5 path @cache
ID 260 gen 30 top level 5 path @log
ID 261 gen 29 top level 5 path @swap

$ btrfs subvolume set-default 256 /mnt

Now we can change root into the mounted system, and start configuring some of the basic options:

$ arch-chroot /mnt

Time

We'll start with configuring time-related settings:

[root@archiso /]# ln -sf /usr/share/zoneinfo/Europe/Amsterdam /etc/localtime
[root@archiso /]# hwclock --systohc

Modify /etc/systemd/timesyncd.conf:

[Time]
NTP=0.arch.pool.ntp.org 1.arch.pool.ntp.org 2.arch.pool.ntp.org 3.arch.pool.ntp.org
FallbackNTP=0.pool.ntp.org 1.pool.ntp.org 0.fr.pool.ntp.org
[root@archiso /]# systemctl enable systemd-timesyncd

Localization

  1. Edit /etc/locale.gen and un-comment en_US.UTF-8 UTF-8.
  2. Run locale-gen
  3. Edit /etc/locale.conf and add LANG=en_US.UTF-8
  4. Edit /etc/vconsole.conf and add KEYMAP=us
  5. Set a hostname in KEYMAP=us

Users

  1. Set a password for the root user:
[root@archiso /]#

New password:
Retype new password:
passwd: password updated successfully
  1. Edit the sudoers file using visudo and uncomment the line:
%wheel ALL=(ALL:ALL) ALL
  1. Add a sudo user:
[root@archiso /]# useradd -m -G wheel vanguard

[root@archiso /]# passwd vanguard

New password:
Retype new password:
passwd: password updated successfully

Hooks

  1. Modify /etc/mkinitcpio.conf and replace the HOOKS= line:
HOOKS=(base systemd autodetect microcode modconf kms keyboard keymap sd-vconsole block sd-encrypt filesystems fsck)
  1. Then regenerate the initramfs:
[root@archiso /]# mkinitcpio -P

Network

We can enable our wired network services:

[root@archiso /]# systemctl enable systemd-networkd systemd-resolved

# Then outside of the `arch-chroot`:

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

Boot Loader

[root@archiso /]# refind-install
ShimSource is none
Installing rEFInd on Linux....
ESP was found at /boot using vfat
Copied rEFInd binary files

Copying sample configuration file as refind.conf; edit this file to configure
rEFInd.

Creating new NVRAM entry
rEFInd is set as the default boot manager.
Creating //boot/refind_linux.conf; edit it to adjust kernel options.

Installation has completed successfully.
[root@archiso /]# efibootmgr
BootCurrent: 0005
Timeout: 1 seconds
BootOrder: 0000
Boot0000* rEFInd Boot Manager	HD(1,GPT,95cbeb76-ad94-47d0-997a-7bf9191e5634,0x800,0x200000)/\EFI\refind\refind_x64.efi

We will need to specify which partition has the encrypted LUKS volume, so let's get its UUID:

[root@archiso boot]# ls /dev/disk/by-uuid -l

total 0
lrwxrwxrwx 1 root root 15 Apr 11 09:13 1E36EDF936EDD1B7 -> ../../nvme0n1p3
lrwxrwxrwx 1 root root 10 Apr 11 09:13 2026-04-01-15-12-08-00 -> ../../sda1
lrwxrwxrwx 1 root root 15 Apr 11 10:21 4868-8986 -> ../../nvme1n1p1
lrwxrwxrwx 1 root root 15 Apr 11 10:28 4db07e4a-2eaa-4edf-ad5c-a0bdaf506c78 -> ../../nvme1n1p2
lrwxrwxrwx 1 root root 10 Apr 11 10:39 603bff8d-9c55-4172-b8cd-249f333416ba -> ../../dm-0
lrwxrwxrwx 1 root root 10 Apr 11 09:13 69CD-35C8 -> ../../sda2
lrwxrwxrwx 1 root root 15 Apr 11 09:13 74CE-AED0 -> ../../nvme0n1p1
lrwxrwxrwx 1 root root 15 Apr 11 09:13 ed678603-70b2-4d47-be53-6b815c67b90e -> ../../nvme0n1p4

Here we can copy 4db07e4a-2eaa-4edf-ad5c-a0bdaf506c78 as it points to /dev/nvme1n1p2, our LUKS volume.

Let's add a boot stanza to /boot/EFI/refind/refind.conf:

menuentry "Arch Linux" {
	icon	EFI/refind/icons/os_linux.png
	volume	"LINUX"
	loader	/vmlinuz-linux
	initrd	/amd-ucode.img
	initrd	/initramfs-linux.img
	options	"rd.luks.name=4db07e4a-2eaa-4edf-ad5c-a0bdaf506c78=root root=/dev/mapper/root rw add_efi_memmap"
}

or we can do systemd boot

[root@anna boot]# cat loader/loader.conf 
default		arch.conf
timeout		0
console-mode	max
editor		no

[root@anna boot]# cat loader/entries/arch.conf 
title	Arch Linux
linux	/vmlinuz-linux
initrd	/amd-ucode.img
initrd	/initramfs-linux.img
options	rd.luks.name=af15dfea-78b7-46a7-a2e0-c8c2603566e0=root root=/dev/mapper/root rw

efibootmgr delete all arch-chroot -S /mnt then bootctl install

  • systemd-boot-update.service

Basic Configuration

Once logged into our sudo-enabled user (part of the wheel group), we can begin customizing our installation.

Wireless

/etc/systemd/network🔒 
❯ cat 10-wlan0.link                             
[Match]
OriginalName=wlan0

[Link] MTUBytes=1420

 install libimobiledevice usbmuxd

/etc/systemd/network🔒 ❯ cat 20-tether.network [Match] Driver=ipheth

[Link] MTUBytes=1420

[Network] DHCP=yes


### Wired Network

We must configure our wired network link:

Modify `/etc/systemd/network/20-wired.network`:

[Match] Name=enp10s0

[Link] RequiredForOnlineRoutable=routable

[Network] DHCP=yes


Then restart the service:

[vanguard@anna ~]$ sudo systemctl restart systemd-networkd


### Secure Boot

Let's install `sbctl` and set up Secure Boot so we can boot Windows again:

[vanguard@anna ~]$ sudo sbctl status

Installed: ✗ sbctl is not installed Setup Mode: ✗ Enabled Secure Boot: ✗ Disabled Vendor Keys: none


First we must create and enroll our own Secure Boot keys:

[vanguard@anna ~]$ sudo sbctl create-keys Created Owner UUID 36a94794-2381-4f35-8d4d-09afe4ffd06f ✓ Secure boot keys created!

[vanguard@anna ~]$ sudo sbctl enroll-keys -m Enrolling keys to EFI variables... With vendor keys from microsoft...✓ Enrolled keys to the EFI variables!


Next we need to make sure all the boot files are signed with our keys:

- sbctl sign -s -o /usr/lib/systemd/boot/efi/systemd-bootx64.efi.signed /usr/lib/systemd/boot/efi/systemd-bootx64.efi

[vanguard@anna ~]$ sudo sbctl verify Verifying file database and EFI images in /boot... ✗ /boot/EFI/refind/refind_x64.efi is not signed ✗ /boot/vmlinuz-linux is not signed

[vanguard@anna ~]$ sudo sbctl sign -s /boot/EFI/refind/refind_x64.efi ✓ Signed /boot/EFI/refind/refind_x64.efi

[vanguard@anna ~]$ sudo sbctl sign -s /boot/vmlinuz-linux ✓ Signed /boot/vmlinuz-linux

[vanguard@anna ~]$ sudo sbctl verify Verifying file database and EFI images in /boot... ✓ /boot/EFI/refind/refind_x64.efi is signed ✓ /boot/vmlinuz-linux is signed


Now our system is ready!


### Snapshot

Time to take a snapshot!


❯ cat /etc/systemd/network/20-tether.network [Match] Driver=ipheth

[Link] MTUBytes=1420

[Network] DHCP=yes

~ ❯ cat /etc/systemd/network/10-wlan0.link
[Match] OriginalName=wlan0

[Link] MTUBytes=1420

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment