We will be installing an Arch Linux system with full disk encryption, hibernation, a splash boot screen and snapshots.
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.
We will need two filesystems:
-
/dev/nvme1n1p1is 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
LINUXwhich will make it easier to refer to this filesystem when we describe its mountpoint in/etc/fstablater on. -
/dev/nvme1n1p2is 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 partThen 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
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
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
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
- Edit
/etc/locale.genand un-commenten_US.UTF-8 UTF-8. - Run
locale-gen - Edit
/etc/locale.confand addLANG=en_US.UTF-8 - Edit
/etc/vconsole.confand addKEYMAP=us - Set a hostname in
KEYMAP=us
- Set a password for the root user:
[root@archiso /]#
New password:
Retype new password:
passwd: password updated successfully
- Edit the
sudoersfile usingvisudoand uncomment the line:
%wheel ALL=(ALL:ALL) ALL
- Add a
sudouser:
[root@archiso /]# useradd -m -G wheel vanguard
[root@archiso /]# passwd vanguard
New password:
Retype new password:
passwd: password updated successfully
- Modify
/etc/mkinitcpio.confand replace theHOOKS=line:
HOOKS=(base systemd autodetect microcode modconf kms keyboard keymap sd-vconsole block sd-encrypt filesystems fsck)
- Then regenerate the initramfs:
[root@archiso /]# mkinitcpio -P
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
[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
Once logged into our sudo-enabled user (part of the wheel group), we can begin customizing our installation.
/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