Phase 4: Bootloader & Reboot
Phase 4: Bootloader & Reboot
Configure mkinitcpio
mkinitcpio generates the initial ramdisk (initramfs) — the tiny Linux environment that runs BEFORE your root filesystem is available. It handles LUKS decryption, btrfs assembly, and hardware detection during early boot.
Verify current HOOKS
# BEFORE: see the default HOOKS line
grep '^HOOKS' /etc/mkinitcpio.conf
Update HOOKS
The HOOKS line controls what runs in the initramfs and in what ORDER. Order matters — encrypt must come before btrfs and filesystems.
# DRY RUN: preview the replacement
sed 's/^HOOKS=.*/HOOKS=(base udev autodetect modconf kms keyboard keymap consolefont block encrypt btrfs filesystems fsck)/' /etc/mkinitcpio.conf | grep '^HOOKS'
# APPLY
sed -i 's/^HOOKS=.*/HOOKS=(base udev autodetect modconf kms keyboard keymap consolefont block encrypt btrfs filesystems fsck)/' /etc/mkinitcpio.conf
# VERIFY
grep '^HOOKS' /etc/mkinitcpio.conf
# Also verify MODULES is empty (NVIDIA modules load via kms hook, not here)
grep '^MODULES' /etc/mkinitcpio.conf
| Hook | Why |
|---|---|
|
Core infrastructure — hardware detection, device nodes |
|
Load modules from |
|
Kernel Mode Setting — early GPU init for smooth boot. Modern nvidia (>=560) supports this |
|
Keyboard input + layout + font for LUKS passphrase prompt |
|
Block device drivers (NVMe, SATA, USB) |
|
LUKS decryption — prompts for passphrase, opens cryptroot. Must be before btrfs/filesystems |
|
Btrfs module loaded early — required for multi-device, good practice for single |
|
Mount the root filesystem |
|
Filesystem check on boot |
Generate initramfs
# -P = generate for ALL presets in all installed kernels
mkinitcpio -P
Enable fallback presets
Arch ships with fallback commented out in the preset files. The preset controls what mkinitcpio -P generates — if fallback isn’t enabled, the fallback boot entry has no image to load.
# BEFORE: see what's configured
grep -n 'PRESETS\|fallback' /etc/mkinitcpio.d/linux.preset
Default state (fallback commented out)
PRESETS=('default')
#PRESETS=('default' 'fallback')
#fallback_image="/boot/initramfs-linux-fallback.img"
#fallback_options="-S autodetect"
Fix linux.preset:
# Comment out default-only PRESETS, uncomment the one with fallback
sed -i "s/^PRESETS=('default')/#PRESETS=('default')/" /etc/mkinitcpio.d/linux.preset
sed -i "s/^#PRESETS=('default' 'fallback')/PRESETS=('default' 'fallback')/" /etc/mkinitcpio.d/linux.preset
# Uncomment fallback image path and options
sed -i 's/^#fallback_image/fallback_image/' /etc/mkinitcpio.d/linux.preset
sed -i 's/^#fallback_options/fallback_options/' /etc/mkinitcpio.d/linux.preset
# VERIFY
grep -n 'PRESETS\|fallback' /etc/mkinitcpio.d/linux.preset
Fix linux-lts.preset (same pattern):
sed -i "s/^PRESETS=('default')/#PRESETS=('default')/" /etc/mkinitcpio.d/linux-lts.preset
sed -i "s/^#PRESETS=('default' 'fallback')/PRESETS=('default' 'fallback')/" /etc/mkinitcpio.d/linux-lts.preset
sed -i 's/^#fallback_image/fallback_image/' /etc/mkinitcpio.d/linux-lts.preset
sed -i 's/^#fallback_options/fallback_options/' /etc/mkinitcpio.d/linux-lts.preset
# VERIFY
grep -n 'PRESETS\|fallback' /etc/mkinitcpio.d/linux-lts.preset
# Regenerate all images (now includes fallback)
mkinitcpio -P
# Verify all 4 images exist
ls -lh /boot/initramfs-*
Actual output (P16g — 4 images confirmed)
-rw------- 1 root root 190M Apr 2 17:48 /boot/initramfs-linux-fallback.img
-rw------- 1 root root 190M Apr 2 17:48 /boot/initramfs-linux-lts-fallback.img
-rw------- 1 root root 132M Apr 2 17:48 /boot/initramfs-linux-lts.img
-rw------- 1 root root 132M Apr 2 17:48 /boot/initramfs-linux.img
-
Default images (~132M) — autodetected modules only (fast, minimal)
-
Fallback images (~190M) — ALL kernel modules included (slower boot, but works when autodetect misses a driver)
Preset verification output (after sed fix)
[root@archiso /]# grep -n 'PRESETS\|fallback' /etc/mkinitcpio.d/linux.preset
7:#PRESETS=('default')
8:PRESETS=('default' 'fallback')
15:#fallback_config="/etc/mkinitcpio.conf"
16:fallback_image="/boot/initramfs-linux-fallback.img"
17:#fallback_uki="/efi/EFI/Linux/arch-linux-fallback.efi"
18:fallback_options="-S autodetect"
Warnings during fallback build (qat_6xxx, aic94xx, bfa, qla2xxx, wd719x, etc.) are safe to ignore — these are firmware for enterprise storage controllers and Intel QuickAssist hardware not present in the P16g. The consolefont warning is cosmetic (no custom font configured).
|
Install systemd-boot
systemd-boot is a simple UEFI boot manager. It reads entries from the EFI partition and presents a menu. Lighter than GRUB, no config generation — you write the entries directly.
# --esp-path = where the EFI System Partition is mounted
# Do NOT use --boot-path — we're not using XBOOTLDR. Everything lives on the ESP.
bootctl --esp-path=/boot/efi install
# Create the entries directory on the ESP (bootctl may not create it without --boot-path)
mkdir -p /boot/efi/loader/entries
| The "world accessible" and "Not booted with EFI" warnings in chroot are cosmetic and expected. The "Running in a chroot, enabling --graceful" message is normal. |
|
Boot entries and kernels MUST be on the same partition Per the Boot Loader Specification: "those files must be located on the same partition" as the .conf entry file, and paths are "absolute paths relative to the root of that file system." This means entries ( Why NOT ext4 XBOOTLDR? The spec states: "the ESP and XBOOTLDR must use a file system readable by the firmware. For most systems this means VFAT." The ThinkPad P16g firmware cannot read ext4 — systemd-boot launches but finds no entries, causing a "reboot to firmware" loop. Field-validated: ThinkPad P16g Gen 3 — ext4 XBOOTLDR (type ea00) did NOT work. Kernels and entries on the ESP (VFAT) = boots correctly. This matches the Razer’s working configuration. |
Loader Configuration
# default = which entry boots automatically after timeout
# timeout = seconds to show the menu (3s is enough to press a key)
# editor no = prevents editing boot params at the menu (security)
cat > /boot/efi/loader/loader.conf << 'EOF'
default arch.conf
timeout 3
console-mode max
editor no
EOF
cat /boot/efi/loader/loader.conf
Capture LUKS UUID
Same variable pattern as crypttab — capture the UUID once, use it in all 3 boot entries.
ROOT_LUKS_UUID=$(blkid -s UUID -o value /dev/nvme0n1p3)
echo "Root LUKS UUID: $ROOT_LUKS_UUID"
Create Boot Entries
Three entries: main kernel (daily driver), fallback (all modules, for debugging), LTS (safety net if mainline breaks NVIDIA).
Kernel parameters explained:
-
cryptdevice=UUID=…:cryptroot— tells theencrypthook which partition to decrypt and what to name the mapper device -
root=/dev/mapper/cryptroot— the decrypted device to mount as root -
rootflags=subvol=@— mount the@btrfs subvolume as/ -
nvidia_drm.modeset=1— enable NVIDIA DRM kernel modesetting (required for Wayland/Hyprland) -
mem_sleep_default=s2idle— use modern standby instead of deep sleep (prevents resume issues on Intel HX + NVIDIA laptops)
Main Entry
cat > /boot/efi/loader/entries/arch.conf << EOF
title Arch Linux
linux /vmlinuz-linux
initrd /intel-ucode.img
initrd /initramfs-linux.img
options cryptdevice=UUID=$ROOT_LUKS_UUID:cryptroot root=/dev/mapper/cryptroot rootflags=subvol=@ rw nvidia_drm.modeset=1 mem_sleep_default=s2idle
EOF
Fallback Entry
# Fallback includes ALL kernel modules (not just autodetected ones)
# Use this if the main entry fails to boot — slower but more compatible
cat > /boot/efi/loader/entries/arch-fallback.conf << EOF
title Arch Linux (fallback)
linux /vmlinuz-linux
initrd /intel-ucode.img
initrd /initramfs-linux-fallback.img
options cryptdevice=UUID=$ROOT_LUKS_UUID:cryptroot root=/dev/mapper/cryptroot rootflags=subvol=@ rw nvidia_drm.modeset=1 mem_sleep_default=s2idle
EOF
LTS Entry
# LTS kernel — if a mainline kernel update breaks NVIDIA or WiFi,
# boot this to get a working system while you troubleshoot
cat > /boot/efi/loader/entries/arch-lts.conf << EOF
title Arch Linux LTS
linux /vmlinuz-linux-lts
initrd /intel-ucode.img
initrd /initramfs-linux-lts.img
options cryptdevice=UUID=$ROOT_LUKS_UUID:cryptroot root=/dev/mapper/cryptroot rootflags=subvol=@ rw nvidia_drm.modeset=1 mem_sleep_default=s2idle
EOF
Verify Boot Entries
# List all entries systemd-boot sees
bootctl list
Expected output (entries on ESP)
type: Boot Loader Specification Type #1 (.conf)
title: Arch Linux (default)
id: arch.conf
source: /boot/efi/loader/entries/arch.conf
linux: /vmlinuz-linux
initrd: /intel-ucode.img
/initramfs-linux.img
options: cryptdevice=UUID=<ROOT-LUKS-UUID>:cryptroot root=/dev/mapper/cryptroot rootflags=subvol=@ rw nvidia_drm.modeset=1 mem_sleep_default=s2idle
type: Boot Loader Specification Type #1 (.conf)
title: Arch Linux LTS
id: arch-lts.conf
linux: /vmlinuz-linux-lts
initrd: /intel-ucode.img
/initramfs-linux-lts.img
type: Boot Loader Specification Type #1 (.conf)
title: Arch Linux (fallback)
id: arch-fallback.conf
linux: /vmlinuz-linux
initrd: /intel-ucode.img
/initramfs-linux-fallback.img
# Verify the UUID is correct in all entries (not a variable name)
grep 'cryptdevice' /boot/efi/loader/entries/*.conf
# Verify kernel images exist on the ESP (where systemd-boot reads them)
ls -lh /boot/efi/vmlinuz-* /boot/efi/initramfs-* /boot/efi/intel-ucode.img
Copy Kernels to ESP
systemd-boot reads kernels from the same partition as the entry files. Since entries are on the ESP, kernels must be there too. The ext4 /boot is for OS-level storage (pacman installs here).
# Copy default kernel images to ESP (no fallback — 512M ESP can't hold all 4)
sudo cp /boot/vmlinuz-linux /boot/efi/
sudo cp /boot/vmlinuz-linux-lts /boot/efi/
sudo cp /boot/initramfs-linux.img /boot/efi/
sudo cp /boot/initramfs-linux-lts.img /boot/efi/
sudo cp /boot/intel-ucode.img /boot/efi/
# Verify — should be ~310M used, ~200M free
ls -lh /boot/efi/vmlinuz-* /boot/efi/initramfs-* /boot/efi/intel-ucode.img
df -h /boot/efi
Pacman Hook: Auto-Sync Kernels to ESP
Without this hook, every kernel or nvidia update requires manually copying files to the ESP. This hook runs automatically after pacman installs/upgrades kernel packages.
sudo mkdir -p /etc/pacman.d/hooks
sudo tee /etc/pacman.d/hooks/99-esp-kernel-sync.hook << 'EOF'
[Trigger]
Type = Path
Operation = Install
Operation = Upgrade
Target = usr/lib/modules/*/vmlinuz
Target = usr/lib/initcpio/*
Target = boot/*
[Action]
Description = Syncing kernels and initramfs to ESP...
When = PostTransaction
Exec = /bin/sh -c 'cp /boot/vmlinuz-* /boot/efi/ && cp /boot/initramfs-linux.img /boot/initramfs-linux-lts.img /boot/intel-ucode.img /boot/efi/'
EOF
# Verify hook exists
cat /etc/pacman.d/hooks/99-esp-kernel-sync.hook
This hook copies only the default initramfs images (not fallback) to keep the ESP under 512M. Fallback images remain on /boot (ext4) only.
|
Register Boot Entry with efibootmgr
Some firmware (ThinkPad) doesn’t auto-detect the systemd-boot EFI binary. Register it explicitly:
efibootmgr --create --disk /dev/nvme0n1 --part 1 --label "Arch Linux" --loader /EFI/systemd/systemd-bootx64.efi
# Verify Arch Linux is first in boot order
efibootmgr -v | head -3
Reboot
# Exit chroot — returns to the live ISO environment
exit
# Unmount everything recursively (order doesn't matter with -R)
umount -R /mnt
reboot
Remove the USB when the system powers off.
|
At boot you will see TWO passphrase prompts:
This is expected. If you configured a keyfile in Phase 3 (optional), you’ll only see one prompt. If boot fails: Press the down arrow at the systemd-boot menu to select "Arch Linux (fallback)" or "Arch Linux LTS". If all entries fail, boot the USB again and |