Build a custom installer ISO

This book will take a slightly unconventional approach to installing NixOS. Instead of using the official NixOS graphical installer ISO, we will build our own custom ISO. While this will take a bit longer the first time you do it, it will greatly enhance your ability to reproduce your environment on any new hardware in the future.

Let’s build a customized, headless NixOS installer ISO that will bake in our personal SSH keys and our WiFi credentials, so that the target machine boots straight onto the home/lab network, and will let us drive the whole install process over SSH, with no monitor or keyboard attached.

To build this custom ISO, you need an existing machine that already has Nix installed; it does not have to be NixOS. The build script itself is launched through nix run nixpkgs#babashka, so Nix is the only requirement on the build host.

Run the script directly over the network, without cloning anything:

[bash]: Run this on your workstation:
nix run --extra-experimental-features "nix-command flakes" \
  nixpkgs#babashka -- \
  -e '(load-string (slurp "https://raw.githubusercontent.com/EnigmaCurry/sway-home/master/bin/nix_build_iso.bb"))'

Or, if you have a clone of the sway-home repository, run it from there:

[bash]: Run this on your workstation:
./bin/nix_build_iso.bb

Answer the questions to customize the installer:

== Customize your NixOS ISO ==
? Installer hostname (nixos-installer)

Enter the installer hostname (default nixos-installer, this will be the host name of the live NixOS USB stick, not the final installation host).

? Build from the default nixpkgs source (github:NixOS/nixpkgs/nixos-26.05)? (Y/n)

Choose the nixpkgs source: the default release channel, another recent release, nixos-unstable, or a custom flake ref. Press Y for the default, which is the only version thats been tested to work.

? Select SSH keys from your ssh-agent (or select none to enter keys manually)
  [x] 256 SHA256:xxxxxxxx/yyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy  (ED25519-SK)
  [ ] 4096 SHA256:xxxxxxx/vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv root@boulder (RSA)
> [x] 256 SHA256:zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz enigma@flux (ED25519)
[spacebar: toggle one, right/left: select all/none, type to filter, ESC to cancel]

Provide your SSH public keys. If you are running an ssh-agent, you can select one or more keys directly from it; if you select none it will offer to let you manually paste keys. The keys are installed for the root user (password login is disabled), so you log in with ssh root@<ip>.

? Pre-seed WiFi credentials into the ISO? Yes
? WiFi SSID
? WiFi PSK (password)
? NetworkManager connection name

Optionally enter your WiFi SSID and password, so the installer joins your wireless network automatically on boot. The key gets baked into the ISO.

? Enable serial console (kernel + serial getty)? (y/N)

Optionally enable a serial console (kernel console + serial getty).

> Enable webhook notify after network-online? Yes
? Webhook URL (will receive JSON POST)     https://my-server-somewhere.example.com/path/to/my/webhook

Optionally enter a webhook URL. Once the network is up, the installer POSTs a JSON message to it containing the hostname, the local IP address, the login user, and a ready-to-use ssh command, so you can find out where it landed on the network. It keeps retrying until it gets a response, so it will still reach you even if the network only comes up later. This is commonly used with something like matrix-hookshot to bridge these notifications to your Matrix chat client.

warning

All of this is burned into the .iso file you build, including your SSH public keys and, if you enable WiFi, your WiFi password in cleartext. Do not publish your .iso file.

After a review screen, the finished ISO is copied to your ~/Downloads directory, named like <hostname>-<username>-custom-<date>.iso.

Create bootable USB stick

Write the .iso file to a USB drive to boot in your target machine. You can use a user friendly image writer app like Fedora Media Writer, or if you know how to identify your USB device file, you can use the venerable dd:

[bash]: Run this on your workstation:
sudo dd if=~/Downloads/name_of_your.iso of=/dev/name_of_your_usb bs=4M status=progress conv=fsync
// settings
theme:
fx: