#!/usr/bin/env bash # # Bootstrap a new Arch system from an installation ISO. # # Bootable USB: # - [Download](https://archlinux.org/download/) ISO and GPG files # - Verify the ISO file: `$ pacman-key -v archlinux--x86_64.iso.sig` # - Create a bootable USB with: `# dd if=archlinux*.iso of=/dev/sdX && sync` # # UEFI setup: # # - Set boot mode to UEFI, disable Legacy mode entirely. # - Temporarily disable Secure Boot. # - Make sure a strong UEFI administrator password is set. # - Delete preloaded OEM keys for Secure Boot, allow custom ones. # # Run installation: # # - Connect to wifi via: `# iwctl station wlan0 connect $SSID` # - Run: `# bash <(curl -sL https://link.rafe.li/dot)` # set -uo pipefail trap 's=$?; echo "$0: Error on line "$LINENO": $BASH_COMMAND"; exit $s' ERR exec 1> >(tee "/root/stdout.log") exec 2> >(tee "/root/stderr.log" >&2) args=() target_device="" new_hostname="" while [[ $# -gt 0 ]] do arg="$1" case "$arg" in "--device") target_device="$2" shift shift ;; "--hostname") new_hostname="$2" shift shift ;; *) args+=("$arg") shift; esac done if [[ -z "$target_device" ]]; then echo "Missing --device argument" >&2 exit 2; fi if [[ -z "$new_hostname" ]]; then echo "Missing --hostname argument" >&2 exit 2; fi if [[ "${#args[@]}" -ne 0 ]]; then echo "Unexpected extra arguments: ${args[*]}" >&2 exit 2 fi if [ ! -f /sys/firmware/efi/fw_platform_size ]; then echo >&2 "You must boot in UEFI mode to continue" exit 2 fi if [[ "$UID" -ne 0 ]]; then echo "This script needs to be run as root!" >&2 exit 3 fi read -rp "THIS SCRIPT WILL OVERWRITE ALL CONTENTS OF ${target_device}. Type uppercase yes to continue: " confirmed if [[ "$confirmed" != "YES" ]]; then echo "aborted" >&2 exit 128 fi timedatectl set-ntp true hwclock --systohc --utc loadkeys de-latin1 # Partition sgdisk --zap-all "${target_device}" sgdisk --clear \ --new 1:0:+550MiB --typecode 1:ef00 --change-name 1:EFI \ --new 2:0:+8GiB --typecode 2:8200 --change-name 2:swap \ --new 3 --typecode 3:8304 --change-name 3:system \ "${target_device}" # Reload partition table sleep 5 partprobe -s "${target_device}" sleep 3 # Encrypt root echo -n "password" | cryptsetup luksFormat --type luks2 --pbkdf argon2id "/dev/disk/by-partlabel/system" echo -n "password" | cryptsetup luksOpen --allow-discards --persistent "/dev/disk/by-partlabel/system" system # Create file systems mkfs.fat -F 32 -n "EFI" /dev/disk/by-partlabel/EFI mkfs.btrfs --force --label system /dev/mapper/system # Mount system subvolume and create additional subvolumes o=defaults,x-mount.mkdir o_btrfs=$o,compress=zstd,ssd,noatime mount -t btrfs LABEL=system /mnt btrfs subvolume create /mnt/@ # / btrfs subvolume create /mnt/@home # /home btrfs subvolume create /mnt/@snapshots # /.snapshots btrfs subvolume create /mnt/@pkg # /var/cache/pacman/pkg btrfs subvolume create /mnt/@aurbuild # /var/lib/aurbuild btrfs subvolume create /mnt/@archbuild # /var/lib/archbuild btrfs subvolume create /mnt/@log # /var/log btrfs subvolume create /mnt/@tmp # /var/tmp umount -R /mnt mount -t btrfs -o subvol=@,$o_btrfs LABEL=system /mnt mount -t btrfs -o subvol=@home,$o_btrfs,nodatacow LABEL=system /mnt/home mount -t btrfs -o subvol=@snapshots,$o_btrfs LABEL=system /mnt/.snapshots mount -t btrfs -o subvol=@pkg,$o_btrfs LABEL=system /mnt/var/cache/pacman/pkg mount -t btrfs -o subvol=@aurbuild,$o_btrfs LABEL=system /mnt/var/lib/aurbuild mount -t btrfs -o subvol=@archbuild,$o_btrfs LABEL=system /mnt/var/lib/archbuild mount -t btrfs -o subvol=@log,$o_btrfs LABEL=system /mnt/var/log mount -t btrfs -o subvol=@tmp,$o_btrfs LABEL=system /mnt/var/tmp # Mount additional partitions mount -o $o LABEL=EFI /mnt/efi # Change default btrfs sub-volume (for DPS) default_subvolume=$(btrfs subvolume list /mnt | grep "path @$" | cut -d ' ' -f2) btrfs subvolume set-default ${default_subvolume} /mnt # Disable CoW for /home due to large loopback files by systemd-homed chattr +C /mnt/home if ! grep "# Installer cache" /etc/pacman.conf > /dev/null; then cat >> /etc/pacman.conf << EOF # Installer cache [options] CacheDir = /mnt/var/cache/pacman/pkg EOF fi # Bootstrap new chroot reflector --country 'Germany' --protocol https --sort age --latest 5 --save /etc/pacman.d/mirrorlist pacstrap /mnt base linux linux-firmware intel-ucode btrfs-progs dracut neovim iwd networkmanager genfstab -L -p /mnt >> /mnt/etc/fstab # Configure timezone, locale, keymap, network sed -i 's/^#en_US\.UTF-8/en_US\.UTF-8/' /mnt/etc/locale.gen sed -i 's/^#de_DE\.UTF-8/de_DE\.UTF-8/' /mnt/etc/locale.gen arch-chroot /mnt locale-gen arch-chroot /mnt systemd-firstboot \ --locale="en_US.UTF-8" \ --keymap="de-latin1" \ --timezone="Europe/Berlin" \ --hostname="${new_hostname}" \ --setup-machine-id echo -e "127.0.0.1\tlocalhost" >> /mnt/etc/hosts echo -e "127.0.1.1\t$new_hostname" >> /mnt/etc/hosts echo -e "\n::1\tlocalhost" >> /mnt/etc/hosts # Use systemd-resolved as dns backend for NetworkManager (auto-detected) ln -sf /run/systemd/resolve/stub-resolv.conf /mnt/etc/resolv.conf # Enable iwd as wifi backend for NetworkManager cat > /mnt/etc/NetworkManager/conf.d/wifi-backend.conf <