Boot and hardware
Boot and hardware
Goal: Document and intentionally configure the lab’s boot loader, kernel packages, and firmware—so recovery and hardware quirks are not mysteries.
Boot experiments can brick a bad disk layout. Prefer the disposable VM, snapshot first, and change one variable at a time.
Why this day exists
When generations fail to boot, the difference between “NixOS is broken” and “I know how to pick last generation / fix EFI” is this literacy. Hardware config is part of ownership.
Theory 1 — Boot chain (UEFI lab)
UEFI firmware
→ EFI system partition (ESP) files
→ boot loader (systemd-boot or GRUB)
→ kernel + initrd (from a generation)
→ stage-1 (find root, unlock, …)
→ stage-2 / systemd
NixOS generations typically install loader entries pointing at store paths for kernel/initrd.
Theory 2 — systemd-boot vs GRUB
| Loader | Common on | Notes |
|---|---|---|
| systemd-boot | UEFI VMs/laptops | Simple, generation entries |
| GRUB | BIOS + UEFI | More knobs; legacy friendly |
# systemd-boot (UEFI)
boot.loader.systemd-boot.enable = true;
boot.loader.efi.canTouchEfiVariables = true;
# GRUB example (do not enable both casually)
# boot.loader.grub.enable = true;
# boot.loader.grub.device = "/dev/vda";Pick one model. Mixing without care is a classic footgun.
Generation UI
- systemd-boot: machine-id entries, often need key to show menu
- GRUB: menu with generation list
Document how your VM displays the menu (ESC, hold space, etc.).
Theory 3 — Kernel packages
boot.kernelPackages = pkgs.linuxPackages; # default channel kernel
# boot.kernelPackages = pkgs.linuxPackages_latest;
# boot.kernelPackages = pkgs.linuxPackages_6_12; # example versioned attr — check 26.05| Choice | Trade-off |
|---|---|
| Default | Matches nixpkgs expectations |
_latest |
Newer hardware; more churn |
| Pinned series | Stability / specific needs |
Kernel modules:
boot.kernelModules = [ ];
boot.initrd.availableKernelModules = [ /* often from hardware-config */ ];
boot.extraModulePackages = [ ];hardware-configuration.nix usually fills initrd modules for disks/filesystems—do not delete blindly.
Theory 4 — Firmware
hardware.enableRedistributableFirmware = true;
# hardware.enableAllFirmware = true; # broader; licensing awarenessVMs often need little firmware; bare metal Wi-Fi/GPU might need redistributable firmware enabled.
Theory 5 — CPU / virt hardware bits
Common NixOS knobs:
# virtualisation-friendly examples (enable only if true for you)
# services.qemuGuest.enable = true;
# virtualisation.hypervGuest.enable = true;Also: microcode updates on real Intel/AMD machines (hardware.cpu.*.updateMicrocode)—relevant on bare metal, less so on many VMs.
Theory 6 — hardware-configuration.nix discipline
| Do | Don’t |
|---|---|
| Import it | Hand-edit UUIDs casually |
| Regenerate after disk changes | Copy another machine’s hardware file |
| Review initrd modules | Assume it never needs refresh |
Regenerate carefully (live media or docs)—wrong root UUID → unbootable.
Theory 7 — Temporary boot parameters
Kernel params:
boot.kernelParams = [ "console=ttyS0" ]; # example serial console for some VMsUseful for debugging; remove noise when done. One change per rebuild when experimenting.
Worked examples bank
Example A — Document current loader
bootctl status 2>/dev/null | head -40
ls /boot 2>/dev/null | head
ls /boot/loader/entries 2>/dev/null | headExample B — Kernel identity
uname -r
nixos-version
ls /run/current-system/kernelExample C — Safe param experiment
Add a harmless boot.kernelParams comment-level change only after snapshot; or set a documented console param if your hypervisor uses serial.
Lab 1 — Boot path document
Create docs/boot.md in the flake repo:
# Boot path — lab
- Firmware: UEFI / BIOS
- Loader: systemd-boot / GRUB
- ESP mount: /boot (options)
- Kernel package option: …
- uname -r: …
- How to open generation menu: …
- Snapshot name: …Fill with real commands’ output.
Lab 2 — Verify loader config matches reality
Compare flake boot.loader.* with bootctl status / GRUB presence. Fix mismatches.
Lab 3 — Kernel package awareness
- Record current
boot.kernelPackages(implicit default if unset).
- Optionally try
linuxPackages_latestin a VM with snapshot.
- Reboot;
uname -r; note difference.
- Revert if not needed.
If you skip the change, still write how you would do it.
Lab 4 — Firmware decision
State in docs:
- VM only → firmware settings chosen
- Bare metal → enable redistributable firmware if devices need it
Rebuild if you change it.
Lab 5 — Recovery dry-run (no panic)
Without breaking the system:
- List generations in bootloader docs
- Rehearse verbally: “If black screen after upgrade, I will…”
- Update the rebuild and generations chapter rollback card with boot-menu specifics
Theory 9 — Initrd and LUKS awareness (preview)
Encrypted roots need initrd unlock configuration. Even if your VM is plaintext:
| Concept | Why note it |
|---|---|
boot.initrd.luks.devices |
Maps UUID → mapped name |
| Prompt vs keyfile | Physical vs automated unlock |
| Wrong UUID | Emergency shell at stage-1 |
Disko/impermanence (Stage VI) will revisit layout; today only name the risk.
Theory 10 — hardware-configuration.nix vs flake purity
hardware-configuration.nix is machine-specific generated state. It belongs in git for the lab host or is generated on first install and kept private per machine.
| Approach | Trade-off |
|---|---|
| Commit lab VM hardware file | Easy clone of this VM |
| Generate per machine | Correct for fleet; clone drill needs care |
Do not copy UUIDs between bare metal and VM casually.
Worked example D — QEMU guest agent (virt)
services.qemuGuest.enable = true; # if hypervisor is QEMU/KVMHelps shutdown/reboots from host tooling. Enable only when accurate.
Lab 6 — Loader entries inventory
bootctl list 2>/dev/null | head -60
ls -la /boot/loader/entries 2>/dev/null | headMap one entry to a NixOS generation number in docs/boot.md.
Lab 7 — Kernel param one-shot (snapshot)
With a VM snapshot:
boot.kernelParams = [ "loglevel=7" ]; # noisier logs; lab onlyRebuild, reboot, confirm cat /proc/cmdline, revert.
Common gotchas
| Symptom | Theory |
|---|---|
| Two boot loaders enabled | Conflicts |
canTouchEfiVariables false in some virt |
May need alternate install path |
| Removed initrd modules | Disk not found |
| Latest kernel + guest tools mismatch | Virt drivers |
Editing /boot by hand |
Lost on rebuild |
Checkpoint
docs/boot.mdcomplete
- Loader in flake matches reality
- Kernel identity recorded
- Firmware policy chosen
- Rollback card updated with boot menu
- Snapshot available before any kernel play
Commit
cd ~/nixos-config
git add .
git commit -m "day20: boot hardware documentation"