Installation
Obtaining the Ghaf Installer
Section titled “Obtaining the Ghaf Installer”Ghaf installer ISO images can be downloaded or built locally as described below.
Currently, only x86_64-linux systems are supported.
To build, for example, the Ghaf installer for Intel laptops (debug variant), run:
nix build .#intel-laptop-debug-installerMaking the Installation Media
Section titled “Making the Installation Media”Plug in a USB stick or external drive large enough to hold the image (16 GB recommended), then follow the instructions for your platform.
From Linux
Section titled “From Linux”-
Identify the device with
lsblkorfdisk -l. Use the device name (e.g.sdb) in place ofsdXin the steps below. -
Unmount all partitions on the device:
Terminal window sudo umount /dev/sdX* -
Write the image to the drive, replacing
path_to_ghaf.isowith the actual path:Terminal window sudo dd if=path_to_ghaf.iso of=/dev/sdX bs=32M status=progress; sync
From macOS
Section titled “From macOS”-
Identify the device with
diskutil list. Use the device name (e.g.disk1) in place ofdiskXin the steps below. -
Unmount all partitions on the device:
Terminal window diskutil unmountDisk diskX -
Write the image to the drive, replacing
path_to_ghaf.isowith the actual path:Terminal window sudo dd if=path_to_ghaf.iso of=/dev/diskX -
Eject the disk once complete:
Terminal window diskutil eject /dev/diskX
From Windows
Section titled “From Windows”- Download and open USBwriter.
- Set the Ghaf ISO as Source.
- Set the USB drive as Target.
- Click Write.
- When finished, safely eject the USB drive.
Booting the Installation Media
Section titled “Booting the Installation Media”Insert the installation media into the target device and boot from it. The interactive installer starts automatically and guides you through the process.
The installer supports the following actions:
- Install Ghaf — write the image to a target device, with optional full-disk encryption and Secure Boot enrollment.
- Wipe disk only — erase a target device without installing.
- Shutdown / Reboot — power options available from the main menu.
If the installer does not start automatically, launch it manually:
sudo ghaf-installer-tuiA non-interactive installer is also available for scripted use:
sudo ghaf-installerInstalling over the Network (Netboot)
Section titled “Installing over the Network (Netboot)”Instead of writing a ~7 GB ISO to a USB stick, the target can boot the installer over PXE and fetch the disk image over HTTP at install time. The boot artefacts are around 650 MB, contain no disk image, and are target-independent — one kernel and initrd serve the whole fleet, and only the image URL differs per target.
Prerequisites
Section titled “Prerequisites”- The target must expose a network boot entry in its firmware, and the firmware must have a UEFI driver for the NIC you intend to boot from. On Lenovo machines this appears as
PXE BOOTin the boot menu. Check from a running system withefibootmgr -v. - The build host and the target must be on the same layer-2 network. PXE uses broadcast; it does not cross a router.
- The build host needs root (DHCP/PXE require ports 67, 69 and 4011).
Building the Artefacts
Section titled “Building the Artefacts”nix build .#intel-laptop-debug-netboot-installer -o result-netbootnix build .#intel-laptop-debug -o resultServing the Install
Section titled “Serving the Install”Always start with --dry-run. It validates the arguments, prints the exact kernel command line and boot plan, and starts nothing:
nix develop --command ghaf-netboot \ --interface eth0 --mac aa:bb:cc:dd:ee:ff \ --netboot result-netboot --image result \ --dry-runWhen the plan looks right, drop --dry-run:
sudo ghaf-netboot \ --interface eth0 --mac aa:bb:cc:dd:ee:ff \ --netboot result-netboot --image result \ --open-firewall --exit-after-serveKey options:
--macis an allowlist and is mandatory. Machines not on it are answered with a 404, which PXE treats as “ignore me”. On a shared network this is the only thing preventing an unrelated machine from booting your installer.--open-firewallopens ports 67, 69, 4011 and the HTTP port for the duration of the run, then closes them again on exit. Without it, a host that filters inbound traffic drops every PXE request before the server sees it — the server logs nothing and the target times out.--exit-after-servestops the server once the image has been transferred in full. It is on by default with--install-target, because an unattended install reboots itself when it finishes: if the target has network boot ahead of its disk, a server left running catches that reboot and reinstalls in a loop.--no-exit-after-serveopts out and warns. For interactive runs it is off, since nothing fetches the image and the server needs to stay up while an operator works through the TUI.--force-interfaceis required when the interface facing the target also carries the default route, which is the normal case on a shared lab network.--ipxeselects the iPXE binary served to 64-bit UEFI clients. The default is thesnponly.efiGhaf builds itself and is almost always what you want; see The iPXE binary below before changing it.
Unattended Installs
Section titled “Unattended Installs”Adding --install-target writes to the given device with no prompts:
sudo ghaf-netboot ... --install-target /dev/nvme0n1When the write finishes, the installer prints a ten-second notice and reboots into the system it just wrote — there is no installation medium to remove and nobody there to remove it. Boot with ghaf.install_noreboot to stay in the installer instead, which is what you want when diagnosing an install that went wrong, since otherwise the evidence reboots away.
How It Works
Section titled “How It Works”The server runs Pixiecore in ProxyDHCP mode, so it never assigns addresses and cannot disturb an existing DHCP server. Pixiecore asks a small local HTTP API what to do with each machine that tries to boot; the API answers only for allowlisted MACs. The installer then fetches ghaf-image.raw.zst and ghaf-image.bmap over HTTP and writes the image with bmaptool, which verifies a SHA-256 per range as it copies. A missing .bmap is fatal rather than falling back to an unverified copy.
The iPXE Binary
Section titled “The iPXE Binary”The target’s firmware does not boot the kernel directly. It first loads iPXE over TFTP, and that iPXE then fetches the boot script, kernel and initrd over HTTP. Which iPXE build gets served matters more than it sounds, because the two obvious choices each fail in a different way:
| iPXE build | NIC driver | Failure |
|---|---|---|
| Pixiecore’s built-in | iPXE’s own native drivers | Broadcast DHCP replies never arrive on some adapters. iPXE retries ten times, reboots, and repeats forever. |
A stock snp.efi / snponly.efi |
firmware SNP | Identifies as user-class iPXE, which Pixiecore reads as plain UEFI firmware, so it re-serves the iPXE binary in an endless chainload loop. |
Ghaf therefore builds its own: bin-x86_64-efi/snponly.efi, which drives the NIC through the firmware’s SNP driver, with an embedded script that sets user-class pixiecore. That user-class string is the entire test Pixiecore applies to decide it has already chainloaded a client, so setting it is what avoids the loop while keeping the MAC allowlist, the 404, and the assembled kernel command line on their normal path.
This is the default, and on a machine whose firmware enumerates the NIC at all it should need no thought. Two escape hatches exist:
--ipxe builtinfalls back to Pixiecore’s own iPXE. Worth trying if the firmware’s SNP does not cover the adapter, at the cost of the broadcast-DHCP problem above.--ipxe <FILE>serves a binary you supply. It must carry an embedded script that sets user-classpixiecore, or it will chainload-loop.
On a non-x86_64 build host the custom binary is not built, and the server falls back to Pixiecore’s built-in iPXE. Such a host can still serve an x86_64 target; it just inherits the first row of the table.