User:Altercation/Bullet Proof Arch Install

From ArchWiki

This article is not officially supported.

The Arch Linux community does not offer support for the information contained in this page; for installation procedures, the Installation guide is the only officially supported document. The content below is mainly maintained by User:Altercation, who last reviewed it on 11 December 2016, and it may be out of date or inaccurate.

This page is a summary of the current process I follow when installing Arch on a new laptop or desktop. My process varies over time, but this serves as my "state of the art" best practice recommendations. I'm open to feedback and suggestions for improvements.

Note: This is entirely based on my own opinions and research. It is opinionated about the best way to do things given the current state of the kernel, hardware availability, and utilities, filesystems, etc. I will explain my rationale for specific choices and provide links to other sources of information (on the Arch wiki or external) where useful.

Objectives

The goals of my standard "bullet proof" Arch Linux installation are:

  • Benefit from Arch's rolling release model while mitigating any risk of system corruption or data loss due to failed upgrades
  • Minimize risk due to hardware failure or total system loss (e.g. theft or physical destruction)
  • Make system rollbacks easy and procedural, using a standard setup and methodology for recovery of previous system (and user data) states

Why not the Arch Install instructions?

The Arch Linux installation instructions are an excellent, general starting point and you should read and understand them. That being said, they are designed to map to a very, very broad set of installation scenarios. I am interested in a very specific type of installation (daily use desktops and laptops in this article) and I have a strong set of opinions about the "right" or "best practice" way to set up my own working systems.

These instructions, then, are much more prescriptive and have strong suggestions or directions about how (and why) one should use a particular method during Arch Linux installation.

Key differences from the stock Arch install instructions

How these instructions differ from the standard Arch guidance:

  • We are encrypting the entire system but not using LVM as is often recommended (see the encryption section below for details about why we are not using it).
  • I use labels and avoid UUIDs where possible to make switching to an alternate recovery drive easier.
  • I skip arch-chroot and simply boot the new system (not reboot, just boot) from within the install environment using systemd-nspawn(1) which enables the *ctl commands to work (and systemd services to be enabled properly without need for a system reboot)
  • I use the various systemd *ctl commands (hostnamectl, timedatectl, localectl, etc.) to configure the system
  • I use the systemd mkinitcpio hooks in lieu of the legacy hooks.
  • I use BTRFS exclusively for root and home partitions for its backup capabilities.
  • I recommend and use secure boot (but not with the Microsoft keys) to minimize risk due to the unavoidably unencrypted EFI partition.

Assumptions

These instructions presuppose the following:

  • fairly new hardware (these instructions may work on older hardware, but there may be unusual issues that crop up... if you aren't comfortable handling those situations you should consider a more "traditional" Arch Linux install (e.g. using grub, LVM, ext4, etc.)
  • UEFI support (BIOS system install is also possible with some modification of these instructions, but this has not yet been detailed here)
  • probably an SSD.
  • some familiarity with Arch, or at least some experience with Linux, will help
Warning: I am assuming you are going to single boot (not dual boot) and that you are willing to wipe all data from the install drive. If that is not the case, do not proceed with these instructions as following them will effectively destroy any data (and other operating systems) on the installation target drive.

Prerequisites

You must have a recent copy of the Arch install USB. See Getting and installing Arch for general information and USB flash installation media for specific details on creating a USB Arch installation drive.

You should also have a reasonable amount of time in which to complete the installation. Novice users will want to budget a couple hours. Experienced users will know to budget a couple hours.

Preparation

Boot from USB Drive

Boot your system from your prepared Arch Linux USB drive. It is important that you boot in EFI mode, not legacy BIOS compatibility mode. See the UEFI article for more information about checking boot mode and ensuring you are booting using EFI.

Bring up the network

If you booted into a system with an ethernet cable plugged in, chances are you're up and running. If you need to use wireless instead, iwctl will more often than not work without trouble. If those don't work, see the Arch installation guide section on connecting to the internet.

Select Drive

I like to use the lsblk command to bring up a quick list of the block devices on the system and identify existing partition structures and sizes, as well as mount points. If you don't already use it, I recommend lsblk when you need information on any given drive or partition. It's useful for polling UUIDs as well. For example, the following command gives you a clearly formatted summary of all block devices, their partition labels, filesystem lables, UUIDs and partition UUIDS: lsblk -o +LABEL,PARTLABEL,UUID,PARTUUID

In any case, the plain command without options should be enough to identify the drive you will install to (for example /dev/sda or /dev/nvme0n1:

# lsblk

Wipe Drive Securely (optional)

While it is not necessary, given that this process will go through the trouble of encrypting the main system partition, it makes sense to do a secure wipe of the drive. This is more or less directly from Dm-crypt/Drive preparation#dm-crypt wipe on an empty disk or partition.

Warning: Be certain of your target device selection as we will be wiping this drive (either securely or, at minimum, destroying all existing partition data).

First, create a temporary encrypted container the full disk (sdX) to be encrypted, e.g. using default encryption parameters and a random key via the --key-file /dev/{u}random option (see also Random number generation):

# cryptsetup open --type plain /dev/sdXY container --key-file /dev/urandom
Note: An interesting page addressing myths about /dev/urandom can be found here

Second, check the container exists:

# fdisk -l
Disk /dev/mapper/container: XXXX MB, XXXXXXXXXX bytes
...
Disk /dev/mapper/container does not contain a valid partition table

Wipe the container with zeros. A use of if=/dev/urandom is not required as the encryption cipher itself generates randomness.

# dd if=/dev/zero of=/dev/mapper/container status=progress bs=1M
dd: writing to ‘/dev/mapper/container’: No space left on device
Tip:
  • Using dd with the bs= option, e.g. bs=1M as above, is frequently used to increase disk throughput of the operation.
  • To perform a check of the operation, zero the partition before creating the wipe container. After the wipe command blockdev --getsize64 /dev/mapper/container can be used to get the exact container size as root. Now od can be used to spotcheck whether the wipe overwrote the zeroed sectors, e.g. od -j containersize - blocksize to view the wipe completed to the end.

Finally, close the temporary container:

# cryptsetup close container

Partition & Format Drive

Understanding some basics about disks, partitions, and filesystems

Think of your drive like a big empty building, no rooms. This is your plain physical drive (either a spinning platter drive or an SSD). Next imagine that to make it useful, we divide the building into apartments. In our analogy the apartments are partitions of the drive.

Imagine also that there are a couple different standard methods of creating layout maps to the apartments. These plans are standardized so that the public services that have to access the building regularly (like the fire and police, for example) understand how to find the apartment entrances. In the world of partitioning, the equivalent is the "partition table scheme". The two common schemes are called "MBR" (Master Boot Record, older) and "GPT" (GUID Partition Table, newer). We will be using the new GPT scheme.

Finally, imagine that the apartments are just empty shells. The act of building walls and laying out the functional structure of the apartments is equivalent to formatting our disk partitions with a filesystem. And just as different layouts may be more or less efficient, or have other different functional characteristics, so to filesystems have different attributes that make them useful in different ways.

Our partition plans

We will take our physical drive and divide it into three partitions. These partitions each serve a distinct purpose. All but the first and smallest, the EFI partition, will be encrypted. For security, if your system supports Secure Boot, you may choose to cryptographically sign the data stored on the EFI partition so that any tampering will be evident, despite the lack of encryption on that partition.

Note: Again, we're assuming an UEFI installation (not legacy BIOS) and a GPT (not MBR) partition structure. If none of that made sense, best to start with the Partitioning article or do some test installs of Arch first. Partitioning is, in my opinion, one of the areas that is most confusing for the new Arch user. Additionally, there is a lot "received wisdom" and cargo-cult assumption making about how best to partition. Working through some of the "this is how it's always been done" tutorials isn't necessarily a bad thing... In many cases the way it has always been done has some sound reasoning behind it, despite being cargo-culted half the time. That being said, the method presented here is different from that in most of the Arch wiki partitioning methodologies.

We will not be using LVM. You read a lot about LUKS and LVM and it all gets a bit complicated (LVM on LUKS, LUKS on LVM, etc.). LVM is a "logical volume manager" and abstracts physical devices (drives) into virtual devices, for easier management. It's a good idea but we will be using btrfs to effectively achieve the same results and really don't need the overhead of LVM.

Partition Summary

  • 1: EFI - The UEFI 'bios" will look for this FAT32 formatted partition and either locate a default bootloader or will locate a specific EFI boot entry on it. This partition is by necessity not encrypted (unless the drive it is on has been encrypted as a "self encrypting drive")
  • 2: Swap - Used by Linux to swap out pages of memory from RAM to disk. Despite debate in this area, it is advisable to have at least some swap space, even it you are not planning on using hibernation on your machine. The Arch wiki is unfortunately rather too brief in its own swap article so I recommend the Fedora documentation on swap for a good overview on determining the right swap partition size.
  • 3: System - This will hold our root and home data. It will be formatted as BTRFS and will use subvolumes to manage snapshots of the current root and home contents.

Visual overview of our partitions, filesystems, and contents

This is a visual summary of our partions (inc. size information), encryption, filesystems, and a summary of the contents of each container.

Partition 1 (EFI)

550MiB+

FAT32
kernel & initramfs

(signed if using secure boot)

Partition 2 (Swap)

w/ Hibernate: Equal RAM, w/o Hibernate: min 8GB

dm-crypt
SWAP
Partition 3 (System)

Remaining Space

LUKS dm-crypt
BTRFS Top Level Subvolume
BTRFS Root Subvolume
BTRFS Home Subvolume
BTRFS Snapshots Subvolume
BTRFS Root Snapshot Subvolume(s)
BTRFS Home Snapshot Subvolume(s)

Partition Drive

There are many utilities available for Partitioning. Because we are using Arch Linux and the command line doesn't hurt us (it makes us stronger) we will be using pure, non-interactive command line tools for this. Specifically, we will be using the utility sgdisk from the gptfdisk package. The fdisk utility now also supports GPT partitioned drives, so it would be an alternative. sgdisk is available by default on the standard Arch install iso that you use to make your bootable USB drive.

First, select the drive you will install to. Make sure you have selected the correct drive!. Again, a quick lsblk will work here.

# DRIVE=/dev/DRIVEID

(replace /dev/DRIVEID with the correct value, for example /dev/sda, /dev/nvme0n1, etc.)

If you didn't securely wipe the drive already, it's worth "zapping" it using sgdisk to remove any lingering legacy partition information. If you already wiped the drive you can skip this command (though it won't hurt to run it again).

Warning: This command will also effectively erase the selected drive!
# sgdisk --zap-all $DRIVE

The following command will then create all three partitions in one go. It will also effectively erase the selected drive!

# sgdisk --clear \
         --new=1:0:+550MiB --typecode=1:ef00 --change-name=1:EFI \
         --new=2:0:+8GiB   --typecode=2:8200 --change-name=2:cryptswap \
         --new=3:0:0       --typecode=3:8300 --change-name=3:cryptsystem \
           $DRIVE

It is worth noting that the "--change-name" values are, in this case, creating GPT "partition labels". You can subsequently see these by using the lsblk -o +PARTLABEL command. It is good to use "EFI" for the efi partition. I am not aware of UEFI implementations that actively use the label for identification of the EFI partition, but it is possible that some do (UEFI bios implementations are not always consistent or to spec). The other two names are entirely "arbitrary". There is nothing special about calling them cryptswap and cryptsystem; they are simply good, clear names that remind us of the purpose of this partition and what it contains (for example, "cryptswap" suggests "this partition is for swap, and is encrypted").

One final note about partition labels: we want them to be unique on your system. If for some reason there is already a GPT partition with the name EFI on another drive on your system, either change that partition name or use something besides "EFI" to avoid a namespace collision.

Note also, that the backslashes at the end of each line can be omitted if you are writing the command on one long line instead. Those simply allow the splitting of a single command onto multiple lines.

Format EFI Partition

Next up: format the first (EFI) partition using the (required) FAT32 filesystem.

# mkfs.fat -F32 -n EFI /dev/disk/by-partlabel/EFI

Note that we here make use of the partition label we just assigned. I prefer doing this since it simplifies scripts significantly for me and makes them, in my opinion, more readable.

Encrypt System Partition

Encrypt the main partition. Use a good passphrase. Note that the "--align-payload" value has been used as per this suggestion/explanation on the dm-crypt mailing list.

Note also that I selected the encryption algorithm and key size using cryptsetup benchmark. See the Dm-crypt/Device_encryption article for more details.

# cryptsetup luksFormat --align-payload=8192 -s 256 -c aes-xts-plain64 /dev/disk/by-partlabel/cryptsystem

After creating the encrypted container, open it. Again, note the use of the partition label to identify the drive. Additionally, note that once we open this device we are giving it a name of "system." Thus "cryptsystem" is the encrypted system partition, while "system" is the name we are using once it has been opened in an unencrypted state. These names are arbitrary (Linux doesn't care what we use) but they help us keep things organized during this process.

# cryptsetup open /dev/disk/by-partlabel/cryptsystem system

Bring Up Encrypted Swap

Finally we create encrypted swap. In this example we are not enabling hibernation. I will provide information on how to enable hibernation separately. Again, using partition labels to identify the partition and going from "cryptswap" to just "swap".

We are not using LUKS here (which effectively makes dm-crypt easier to use). We're just using plain dm-crypt to encrypt the swap partition using a random key.

# cryptsetup open --type plain --key-file /dev/urandom /dev/disk/by-partlabel/cryptswap swap
# mkswap -L swap /dev/mapper/swap
# swapon -L swap

Create and mount BTRFS subvolumes

Now on to the main attraction: creating our BTRFS subvolume structure. While BTRFS can be set up like any other filesystem (single command, just use the created file system directly), we're going to use the power of BTRFS to enable snapshotting our system state efficiently and rollbacks as necessary.

First we create a top-level BTRFS subvolume. Note that the top-level entity in BTRFS nomenclature is still referred to as a subvolume, despite being at the top-level.

We will create and mount this subvolume, create some new subvolumes inside it, and then switch to those subvolumes as our proper, mounted filesystems. Doing this will enable us to treat our root filesystem as a snapshotable object.

Top-level subvolume creation.

# mkfs.btrfs --label system /dev/mapper/system

Temporarily mount our top-level volume for further subvolume creation. Note that the variable 'o' in this case is our default set of options for any given filesystem mount, while "o_btrfs" are those plus some options specific to btrfs. The default option "x-mount.mkdir" is a neat trick that allows us to skip the creation of directories for mountpoints (they will be created automatically). We assume /mnt as the standard mount point, as in a normal Arch Linux installation.

# o=defaults,x-mount.mkdir
# o_btrfs=$o,compress=lzo,ssd,noatime

Note the use of our filesystem label to mount our subvolume. This is distinct from the partition labels used earlier. See the Arch wiki article on Persistent_block_device_naming for more information.

# mount -t btrfs LABEL=system /mnt

Now we create the subvolumes which will actually be mounted in our running system:

# btrfs subvolume create /mnt/@root
# btrfs subvolume create /mnt/@home
# btrfs subvolume create /mnt/@snapshots

Then we unmount everything...

# umount -R /mnt

And remount just the subvolumes under our top-level subvolume (which remains unmounted unless we need to do "surgery" and rollback to a previous system system):

# mount -t btrfs -o subvol=@root,$o_btrfs LABEL=system /mnt

Before mounting the home and snapshots subvolumes, let's walk through that command so we understand what it's doing:

The 'mount -t btrfs' just specifies that our filesystem is of type BTRFS. This is often not necessary since mount will attempt to identify the filesystem type, but being explicit is often the best strategy with command line utilities, so we identify the type here.

We use our previously defined mount options with the $o_btrfs variable and add the additional option "subvol=@root" (separating it from our other defaults with a comma, of course). The next part of the command is "LABEL=system". Combined this reads as "mount the filesystem with the 'system' label, and use the 'root' subvolume within that".

Finally, all this gets mounted at our install root, which in this case is "/mnt".

Now let's pick back up and mount our home and snapshots subvolumes. Note that while the subvolume name is "@snapshots", we will mount it at ".snapshots" (note dot prefix for the mount point). This will keep our root directory listing clean but will still make it available at a reasonable mount location.

# mount -t btrfs -o subvol=@home,$o_btrfs LABEL=system /mnt/home
# mount -t btrfs -o subvol=@snapshots,$o_btrfs LABEL=system /mnt/.snapshots

It it worth noting that in each case we are telling mount to use the same filesystem, namely the one labeled "system". However in each case we are also telling it to look for a different subvolume in that filesystem (via the "subvol=" option).

With this done, we can move on to installing the actual system.

Mount EFI partition

First create the folder /mnt/boot

# mkdir /mnt/boot

then mount the partition

# mount LABEL=EFI /mnt/boot

Installation of Base Arch Linux System

We are currently running off the Arch installer root filesystem. We will first use the 'pacstrap' utility to start our installation and then we will boot into our new minimal system using systemd-nspawn.

Install base package group

# pacstrap /mnt base linux linux-firmware

fstab Generation and Modification

Note: In a simpler, non encrypted configuration it is possible to skip making an fstab altogether due to a standard known as the Discoverable Partitions Spec. However given the complexity of our encrypted, btrfs subvolume configuration, we require an fstab at this time. It is possible that in future we might feasibly do away with the fstab almost entirely in this setup (see the 'What about automatic mounting of btrfs subvolumes' section on that page).

Create an fstab filesystem table file, using labels (-L) to identify the filesystems.

# genfstab -L -p /mnt >> /mnt/etc/fstab

I prefer to use labels as this makes the system more portable to a backup drive. You should end up with something that looks pretty close to this:

# cat /mnt/etc/fstab
# /dev/mapper/system UUID=xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
LABEL=system        	/         	btrfs     	rw,noatime,compress=lzo,ssd,space_cache,subvolid=257,subvol=/root,subvol=root	0 0

# /dev/mapper/system UUID=xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
LABEL=system        	/home     	btrfs     	rw,noatime,compress=lzo,ssd,space_cache,subvolid=258,subvol=/home,subvol=home	0 0

# /dev/mapper/system UUID=xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
LABEL=system        	/.snapshots	btrfs     	rw,noatime,compress=lzo,ssd,space_cache,subvolid=259,subvol=/snapshots,subvol=snapshots	0 0

# /dev/nvme0n1p1 UUID=xxxx-xxxx
LABEL=EFI           	/boot/EFI 	vfat      	rw,relatime,fmask=0022,dmask=0022,codepage=437,iocharset=iso8859-1,shortname=mixed,errors=remount-ro	0 2

# /dev/mapper/swap UUID=xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
LABEL=swap          	none      	swap      	defaults  	0 0

There is one problem, however. Swap will not remount this way (the plain dm-crypt partition with a random key will not have a label when it is recreated on reboot). You will have to change that line from this:

LABEL=swap          	none      	swap      	defaults  	0 0

to this:

/dev/mapper/cryptswap    	none      	swap      	sw  	0 0

This will ensure that the mapped device will opened as swap successfully. The other lines may remain unchanged.

(NOTE by maximevince: /dev/mapper/swap should probably be used in the aboved fstab, instead of /dev/mapper/cryptswap)

Use sed instead

As a (scriptable) alternative to the manual editing of your /etc/fstab you could use sed(1) to edit the file in place:

# sed -i s+LABEL=swap+/dev/mapper/swap+ /mnt/etc/fstab


in /etc/crypttab we must add the part to mount at boot, so just add :

cryptswap        /dev/disk/by-partlabel/cryptswap        /dev/urandom        swap,offset=2048,cipher=aes-xts-plain64,size=256

Boot into new system

Warning: While this works, I am fairly certain I'll want to double check some things before declaring this step final. Specifically I want to consider some bind mounts and whether there is anything that should be r/w instead of r/o as is normal in a systemd-nspawn container.
# systemd-nspawn -bD /mnt

This will boot your new base Arch Linux system. After the standard boot messages scroll by you will be presented with a login (enter root and hit enter to login).

Tip: If the login fails with "Login incorrect", the problem is likely the securetty TTY device whitelist. Add pts/0 through pts/9 to the container's version of the file (/mnt/etc/securetty) and retry. See FS#45903 for details.

Generate and set locale

Next, edit and uncomment your desired locale(s) from /etc/locale.gen. We use vi in the example below, but you could use a simpler editor such as nano if you wish.

vi /etc/locale.gen
...
#en_PH.UTF-8 UTF-8
#en_PH ISO-8859-1
#en_SG.UTF-8 UTF-8
#en_SG ISO-8859-1
#en_US.UTF-8 UTF-8
#en_US ISO-8859-1
#en_ZA.UTF-8 UTF-8
#en_ZA ISO-8859-1
#en_ZM UTF-8
...

Changing it to (for en_US.UTF-8 as a locale in this example):

...
#en_PH.UTF-8 UTF-8
#en_PH ISO-8859-1
#en_SG.UTF-8 UTF-8
#en_SG ISO-8859-1
en_US.UTF-8 UTF-8
#en_US ISO-8859-1
#en_ZA.UTF-8 UTF-8
#en_ZA ISO-8859-1
#en_ZM UTF-8
...

Alternately one could simply append a known value to the /etc/locale.gen file:

# echo "en_US.UTF-8 UTF-8" >> /etc/locale.gen

After either method, one runs the locale-gen command to generate the locale files:

# locale-gen

Finally we use systemd-firstboot(1) or localectl(1) to set the system locale. Each of these effectively does the same thing which is to simply write to /etc/locale.conf (which can simply be edited directly as per standard Arch install instructions).

With systemd-firstboot

systemd-firstboot(1) will prompt us for selection from our generated locales. It will only work if there is no assigned system locale. If you wish to make changes afterward you can use localectl(1)

# systemd-firstboot --prompt-locale

With localectl

This is an alternate to using systemd-firstboot --prompt-locale. It may also be used subsequent to that command if further changes to the locale are desired.

If you need a reminder of which locales have been installed (during this install or later during system changes), use localectl list-locales:

# localectl list-locales
en_US.UTF-8

We then use localectl set-locale:

# localectl set-locale LANG=en_US.UTF-8

Time and Date

Unless you have a reason not to, we'll turn on NTP synchronization.

# timedatectl set-ntp 1

Then list timezones and pick one

# timedatectl list-timezones
...
America/Kentucky/Louisville
America/Kentucky/Monticello
America/Kralendijk
America/La_Paz
America/Lima
America/Los_Angeles
America/Lower_Princes
America/Maceio
America/Managua
America/Manaus
America/Marigot
America/Martinique
America/Matamoros
...
# timedatectl set-timezone America/Los_Angeles

Set hostname

# hostnamectl set-hostname myhostname

See the man page for hostnamectl(1) for other attributes that can be set. You might also add the hostname to hosts(5):

/etc/hosts
127.0.0.1	localhost.localdomain	localhost
::1		localhost.localdomain	localhost
127.0.1.1	myhostname.localdomain	myhostname

Use echo for a scriptable solution:

# echo "127.0.1.1	myhostname.localdomain	myhostname" >> /etc/hosts

Network configuration

Configure the network for the newly installed environment: see Network configuration.

For Wireless configuration, install the iw, wpa_supplicant, and dialog packages, as well as needed firmware packages.

Base Package Installation

We've already included the base group which is, strictly speaking, all you need to boot a minimal Arch Linux system. At this stage, however, we can install some useful utilities (and Xorg or Wayland, etc.).

# pacman -Syu linux-zen base-devel btrfs-progs iw gptfdisk zsh vim terminus-font

Initramfs

Creating a new initramfs is usually not required, because mkinitcpio was run on installation of the linux package with pacstrap. However we'll need to make some changes to the hooks used on our system. Additionally, I switched over to using the systemd hooks in mkinitcpio, so that is largely what you'll see in my example below.

Edit your {ic|/etc/mkinitcpio.conf}} file and change the line with HOOKS to:

HOOKS=(base systemd sd-vconsole modconf keyboard block filesystems btrfs sd-encrypt fsck)

Finally, recreate the initramfs image:

# mkinitcpio -p linux

Bootloader

Warning: THIS SECTION IN PROGRESS

ReFind Installation / UEFI

Install ReFind with

# pacman -S refind-efi

Then run the command

# refind-install --usedefault /dev/<your_efi_drive> --alldrivers

or if the disk is not detected because you booted on the new system (or other reasons), just refind-install witout the --usedefault, refind will find the boot mountpoint

# refind-install
Note: Please keep in mind that --alldrivers is a safe option if you Dual Boot, have often boot from USB Sticks and so on. But it's not recommended by the ReFind author - see ReFind - Using EFI Driver for source and alternatives

Now spin up your favourite text editor and edit /boot/EFI/refind/refind.conf

First move the existent one:

# cd /boot/EFI/refind/ && mv refind.conf refind.bak

And create a new refind.conf:

# touch refind.conf && vim refind.conf
# refind.conf
timeout          20               # Timeout how long ReFind wait for user input
#include         themes/rEFInd-   # For theming ReFind uncomment this and fill in the right location of your theme
use_graphics_for windows          # Specify the simpler "mac-style" behaviour
also_scan_dirs   +,@/             # Search for boot loaders in the specified directory  


Edit the refind_linux.conf file located in /boot/ - focus on the first line:

# /boot/refind_linux.conf
"Boot with standard options"  "rd.luks.name=*FILL IN UUID FROM PARTITION*=cryptsystem root=UUID=*UUID FROM encrypted root subvolume* rootflags=subvol=@root initrd=/intel-ucode.img initrd=/initramfs-linux-zen.img"
...


Note: You can obtain the subvolume UUID with the command:
# btrfs filesystem show system

and the part UUID with:

# lsblk -fs
Note: You can delete initrd=/intel-ucode.img if you don't have any Intel Hardware in your computer or you didn't install the Intel Microcode Updates.

Root password

Set the root password:

# passwd

Leave the systemd-nspawn environment

Issue a poweroff to exit the nspawned environment:

# poweroff

This will return you the Arch installer environment where you can wrap up.

Legacy boot loader

If your system is not UEFI and you need to use a legacy installer like grub, it will need to be installed from within an arch-chroot generated chroot, not the systemd-nspawn running container. An example of this would be (assuming that the drive of the new system is at /dev/sdb):

# arch-chroot /mnt
# grub-install $DRIVE
# grub-mkconfig -o /boot/grub/grub.cfg
# exit

Command Summary

The following are all critical commands (exluded: the initial drive wipe which is optional). It assumes you have successfully brought up networking prior to starting this sequence.


DRIVE=/dev/DRIVEID

sgdisk --zap-all $DRIVE

sgdisk --clear \
       --new=1:0:+550MiB --typecode=1:ef00 --change-name=1:EFI \
       --new=2:0:+8GiB   --typecode=2:8200 --change-name=2:cryptswap \
       --new=3:0:0       --typecode=3:8300 --change-name=3:cryptsystem \
         $DRIVE

mkfs.fat -F32 -n EFI /dev/disk/by-partlabel/EFI

cryptsetup luksFormat --align-payload=8192 -s 256 -c aes-xts-plain64 /dev/disk/by-partlabel/cryptsystem

cryptsetup open /dev/disk/by-partlabel/cryptsystem system

cryptsetup open --type plain --key-file /dev/urandom /dev/disk/by-partlabel/cryptswap swap

mkswap -L swap /dev/mapper/swap

swapon -L swap

mkfs.btrfs --force --label system /dev/mapper/system

o=defaults,x-mount.mkdir

o_btrfs=$o,compress=lzo,ssd,noatime

mount -t btrfs LABEL=system /mnt

btrfs subvolume create /mnt/root

btrfs subvolume create /mnt/home

btrfs subvolume create /mnt/snapshots

umount -R /mnt

mount -t btrfs -o subvol=root,$o_btrfs LABEL=system /mnt

mount -t btrfs -o subvol=home,$o_btrfs LABEL=system /mnt/home

mount -t btrfs -o subvol=snapshots,$o_btrfs LABEL=system /mnt/.snapshots

pacstrap /mnt base

genfstab -L -p /mnt >> /mnt/etc/fstab

sed -i "s+LABEL=swap+/dev/mapper/swap" /mnt/etc/fstab

systemd-nspawn -bD /mnt

echo "en_US.UTF-8 UTF-8" >> /etc/locale.gen

locale-gen

localectl set-locale LANG=en_US.UTF-8

timedatectl set-ntp 1

timedatectl set-timezone America/Los_Angeles

hostnamectl set-hostname myhostname

echo "127.0.1.1	myhostname.localdomain	myhostname" >> /etc/hosts

pacman -Syu base-devel btrfs-progs iw gptfdisk zsh vim terminus-font

Quick and Dirty

Note: an extra example and may be moved to its own wiki page later

This is the approximate process I follow if I want to bring up Arch on, say, an old laptop for a quick purpose built test machine, etc. It's not an install that I consider long term maintainable since rollbacks aren't implemented, nor is there any encryption, but it's a good example of how simple installation can be.

DRIVE=/dev/DRIVEID
sgdisk --zap-all $DRIVE
mkfs.btrfs -f $DRIVE
mount -t btrfs $DRIVE /mnt
pacstrap /mnt base grub
systemd-nspawn -bD /mnt
echo "en_US.UTF-8 UTF-8" >> /etc/locale.gen
locale-gen
localectl set-locale LANG=en_US.UTF-8
timedatectl set-ntp 1
timedatectl set-timezone America/Los_Angeles
hostnamectl set-hostname myhostname
poweroff
arch-chroot /mnt
grub-install --root-directory=/mnt $DRIVE
grub-mkconfig -o /mnt/boot/grub/grub.cfg
exit
reboot