Prepare the disk
This is the first step that writes to the target machine. Up to now everything lived in RAM and a power cycle undid it; this step destroys whatever was on the disk you picked.
What it builds
A single GPT disk with two partitions:
| Partition | Size | Format | Mounted at | Holds |
|---|---|---|---|---|
| 1 | 1 GiB | FAT32 | /boot |
The bootloader, kernel and initramfs |
| 2 | rest | btrfs | / etc. |
Everything else, optionally encrypted |
The same two partitions serve both architectures, for different
reasons. On an x86_64 PC the first is the EFI System Partition the
firmware reads. On a Raspberry Pi 5 it is the firmware partition,
where the Pi’s bootloader expects config.txt, the kernel and the
device trees; the Pi ignores partition type codes and simply uses the
first FAT partition. A gibibyte is far more than one kernel needs, and
that is the point: boot partitions that are too small are a well-worn
regret.
Everything else goes on btrfs, split into subvolumes. A subvolume is an independently mountable tree inside one filesystem: they all draw from the same free space, so there is no sizing decision to regret, but each can be snapshotted, mounted or rolled back on its own.
| Subvolume | Mounted at | Why it is separate |
|---|---|---|
@ |
/ |
The system itself – the thing you roll back |
@home |
/home |
Your data, which must survive a rollback of the system |
@snapshots |
/.snapshots |
Where snapshots are kept; cannot live inside @ |
@log |
/var/log |
Logs of what went wrong should outlive the rollback |
@pkg |
/var/cache/pacman/pkg |
A re-downloadable cache, not worth snapshotting |
@swap |
/swap |
A swapfile cannot sit on a subvolume that gets snapshotted |
@nix |
/nix |
The Nix store, if you install the desktop: big, and Nix’s own |
Every subvolume except @swap is mounted noatime,compress=zstd:1.
noatime stops a write on every read, which is pure write
amplification on a workstation. zstd at level 1 is fast enough to be
effectively free, while higher levels cost noticeably more CPU for a
few percent more space. @swap is mounted without compression,
because a swapfile must be a plain, uncompressed, un-copy-on-write
extent.
Run it
setup disk
It reads the host file and shows you what it is about to destroy before it destroys it:
================ Review =================
Host: myhost
Disk: /dev/disk/by-id/nvme-XP1000F256G_2213E5A1B2C3
(nvme0n1 238.5G XP1000F256G)
Currently: 2 partition(s): nvme0n1p1 vfat ESP; nvme0n1p2 btrfs arch;
Will create: 1G ESP (vfat) + rest btrfs, no encryption
Subvolumes: @ @home @snapshots @log @pkg @swap @nix
Swap: 8g swapfile
=========================================
This ERASES /dev/nvme0n1. Type the hostname to continue: myhost
The Currently: line is there so that you notice when the disk you
picked is not the one you thought it was – on a machine you are
operating blind, over SSH, from another room, it is worth being slower
than feels necessary. Typing the hostname rather than y is
deliberate friction, and --yes does not answer it. To script it,
pass --confirm-wipe myhost.
Before it gets this far, setup disk refuses to run at all if the
machine booted in legacy BIOS mode, if any partition on the disk is
mounted or in use as swap, or if the disk is the stick itself.
Then it runs, echoing each command as it goes:
+ sgdisk --zap-all /dev/nvme0n1 + sgdisk --clear --new=1:0:+1G --typecode=1:ef00 --change-name=1:ESP --new=2:0:0 --typecode=2:8300 --change-name=2:root /dev/nvme0n1 + partprobe /dev/nvme0n1 + udevadm settle + wipefs --all --quiet /dev/nvme0n1p1 /dev/nvme0n1p2 + mkfs.fat -F32 -n ESP /dev/nvme0n1p1 + mkfs.btrfs --quiet -L arch /dev/nvme0n1p2 + mount /dev/nvme0n1p2 /mnt + btrfs subvolume create /mnt/@ ... six more subvolumes, then umount /mnt and the eight mounts + btrfs filesystem mkswapfile --size 8g --uuid clear /mnt/swap/swapfile + swapon /mnt/swap/swapfile ✓ disk 8 mounts under /mnt, 8g swap active Next: setup install
Reading it across:
sgdisk --zap-alldestroys the old GPT and any leftover MBR.--new=1:0:+1Gmakes partition 1 a gibibyte from the first aligned sector,--new=2:0:0gives partition 2 the rest, and the type codes mark them as an EFI System Partition (ef00) and a Linux filesystem (8300).wipefsis the step most install guides leave out. The new partitions start exactly where the old ones did, so on a reinstall the old btrfs signature is still sitting at the start of partition 2, andmkfs.btrfsrefuses to format over it.- The filesystem labels (
ESP,arch) are a convenience: they makelsblkreadable. - The subvolumes are created at the top level of the filesystem,
which is then unmounted and never used again except as their
container.
@is mounted first, because the mount points for the others are directories inside it. - The ESP is mounted
fmask=0077,dmask=0077, readable by root only, andgenfstabcarries that into the fstab. FAT has no permissions of its own, and with the default masks systemd-boot’s random seed is world-readable, whichbootctlreports at every boot as “a security hole”. - The swap is turned on now, not just created, for two reasons: on a low-memory machine it is what keeps the install from running out, and the fstab is generated from what is active, so a swapfile that is not on would be silently left out of it.
On the Pi, mkswapfile reports page size 16384 rather than 4096,
because linux-rpi-16k is built with 16 KiB pages. That is expected.
It does leave one trap: a swapfile records the page size it was made
with, and a kernel with a different page size refuses it. If you ever
move this disk to a generic 4 KiB-page arm64 kernel, swapon fails
with Invalid argument; delete the swapfile and make a new one.
With encryption
If the host file says ENCRYPT=yes, two commands join the list before
mkfs.btrfs:
+ cryptsetup luksFormat --type luks2 --batch-mode --verify-passphrase /dev/nvme0n1p2 + cryptsetup open /dev/nvme0n1p2 cryptroot
The first asks you to set and confirm the passphrase; the second asks
for it again to unlock the container, which makes
/dev/mapper/cryptroot appear, and everything after that formats and
mounts the mapper device instead of the partition. LUKS2’s defaults
are good ones – aes-xts-plain64, and argon2id as the key
derivation function, which is memory-hard and therefore expensive to
attack with GPUs – so setup does not choose any of them for you.
There is no backdoor and no reset. If you forget it, the data is gone – that is the entire purpose. Put it in a password manager before you reboot, not after.
Running it again
If /mnt already holds the layout setup disk would build, it says
so and touches nothing:
/mnt already has the layout for myhost. Nothing to do.
If the layout exists on the disk but is not mounted – the live system
rebooted, or you are coming back to finish a half-done install –
--mount puts it back without formatting, unlocking LUKS first if
the host file says the disk is encrypted:
setup disk --mount
That is also the rescue path for a machine that will not boot: boot
the stick, run setup host --from ... and setup disk --mount, and
arch-chroot /mnt puts you inside it.