From 90a23a8c967c6f5603ca76becf4c9ce2509f8d83 Mon Sep 17 00:00:00 2001 From: Miles Ward Date: Wed, 6 May 2026 14:13:59 -0400 Subject: [PATCH] docs(runbook): apply OpenPXE rebrand to network-boot runbook MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The Linux network-boot runbook landed on origin/main while the v0.3.0 rebrand was in flight on local main. Runs the same string-rewrite pass: PXEForge → OpenPXE, Gated → Queued, /api/gate → /api/queue, PXEFORGE_ env vars → OPENPXE_, container path under /var/lib/openpxe. --- runbooks/linux-network-boot.md | 82 +++++++++++++++++----------------- 1 file changed, 41 insertions(+), 41 deletions(-) diff --git a/runbooks/linux-network-boot.md b/runbooks/linux-network-boot.md index b2b0510..5e60b76 100644 --- a/runbooks/linux-network-boot.md +++ b/runbooks/linux-network-boot.md @@ -1,13 +1,13 @@ # Runbook: Boot a Linux machine from an ISO over the network -End-to-end walkthrough: spin up PXEForge, load an Ubuntu (or any +End-to-end walkthrough: spin up OpenPXE, load an Ubuntu (or any Linux) ISO into it, target a specific bare-metal or VM client by its MAC address, and have that machine PXE-boot the installer over the LAN — no USB stick, no console babysitting. This runbook assumes: -- You have **one Linux host** to run the PXEForge container (any +- You have **one Linux host** to run the OpenPXE container (any distro with Docker / Podman; 2 GB RAM, ~50 GB disk for the ISO library). - That host sits on the **same broadcast domain / VLAN** as the @@ -15,7 +15,7 @@ This runbook assumes: networks need a DHCP relay and are out of scope here. - An **existing DHCP server** is already handing out IP leases on that VLAN (your home router, OPNsense, Windows Server, etc.). - PXEForge runs as a *DHCP proxy* — it never leases IPs, it only + OpenPXE runs as a *DHCP proxy* — it never leases IPs, it only layers the boot information on top of the existing DHCP exchange. - The target client is configured to **PXE-boot** in BIOS/UEFI firmware (usually `F12` boot menu → Network, or set as first boot @@ -28,7 +28,7 @@ or [docs/architecture.md](../docs/architecture.md) first. ## 0. Pick your host’s LAN IP -You need the IPv4 address PXEForge will advertise to clients. From +You need the IPv4 address OpenPXE will advertise to clients. From the host: ```bash @@ -40,33 +40,33 @@ example `10.0.0.5/24` on `eno1`. From here on we call it `PXE_HOST_IP`. > **Why this matters.** Every URL handed to clients (TFTP server, -> iPXE chain URL, ISO URL) is built from this IP. If PXEForge +> iPXE chain URL, ISO URL) is built from this IP. If OpenPXE > auto-detects the wrong interface or loopback, clients will fetch > from an unreachable address and silently fail. The startup will > *fail loudly* if it can only auto-detect a loopback address. --- -## 1. Run PXEForge +## 1. Run OpenPXE The MVP path is a single `docker run` against the published image, with `--network host` so the container can see DHCP broadcasts on the LAN. ```bash -mkdir -p ~/pxeforge/isos ~/pxeforge/work +mkdir -p ~/openpxe/isos ~/openpxe/work -docker run -d --name pxeforge \ +docker run -d --name openpxe \ --restart unless-stopped \ --network host \ - -e PXEFORGE_PUBLIC_IP=10.0.0.5 \ - -e PXEFORGE_DHCP_MODE=proxy \ - -v ~/pxeforge/isos:/var/lib/pxeforge/isos \ - -v ~/pxeforge/work:/var/lib/pxeforge/work \ - ghcr.io/YOUR-ORG/pxeforge:0.2.0 + -e OPENPXE_PUBLIC_IP=10.0.0.5 \ + -e OPENPXE_DHCP_MODE=proxy \ + -v ~/openpxe/isos:/var/lib/openpxe/isos \ + -v ~/openpxe/work:/var/lib/openpxe/work \ + ghcr.io/YOUR-ORG/openpxe:0.2.0 ``` -Substitute your `PXEFORGE_PUBLIC_IP`, of course. If you’re building +Substitute your `OPENPXE_PUBLIC_IP`, of course. If you’re building from this repo instead of pulling, see the [README quick start](../README.md#quick-start--mvp-container-recommended). @@ -84,7 +84,7 @@ otherwise. See [README — Container health probes](../README.md#container-healt ### Check the listening ports -PXEForge holds three privileged UDP/TCP ports. From another shell on +OpenPXE holds three privileged UDP/TCP ports. From another shell on the host: ```bash @@ -93,7 +93,7 @@ sudo ss -lntp | grep ':80\b' # HTTP UI / boot scripts ``` All four should be present. If port 67 is taken by `dnsmasq` or the -host’s own DHCP, stop that service or run PXEForge on a separate box — +host’s own DHCP, stop that service or run OpenPXE on a separate box — two listeners on `:67` will fight. --- @@ -114,7 +114,7 @@ Two options. Pick one. - File size and SHA-256 Big ISOs stream — there is no 2 GB limit, but expect upload to be -gated by your browser ↔ host link. The UI shows a progress bar; the +throttled by your browser ↔ host link. The UI shows a progress bar; the animated anvil on the Dashboard tab fires up while imaging is in flight. @@ -124,17 +124,17 @@ If you already have a folder of ISOs on the host, skip the browser: ```bash # Dry run first — see what would be imported, no writes: -docker exec pxeforge pxeforge seed \ +docker exec openpxe openpxe seed \ --from /seed \ --dry-run # For real, mount the source dir read-only into the container: docker run --rm \ -v /my/iso-library:/seed:ro \ - -v ~/pxeforge/isos:/var/lib/pxeforge/isos \ - -v ~/pxeforge/work:/var/lib/pxeforge/work \ - -e PXEFORGE_PUBLIC_IP=10.0.0.5 \ - ghcr.io/YOUR-ORG/pxeforge:0.2.0 seed --from /seed + -v ~/openpxe/isos:/var/lib/openpxe/isos \ + -v ~/openpxe/work:/var/lib/openpxe/work \ + -e OPENPXE_PUBLIC_IP=10.0.0.5 \ + ghcr.io/YOUR-ORG/openpxe:0.2.0 seed --from /seed ``` Each `*.iso` in `/seed` runs through the same upload pipeline as the @@ -182,13 +182,13 @@ Most BIOS/UEFI screens display the NIC MAC during the network-boot attempt — usually as `MAC: AA-BB-CC-DD-EE-FF` flashing on the splash right before "PXE-E53: No boot filename received". Write it down. -### 3c. By letting it boot once and watching PXEForge +### 3c. By letting it boot once and watching OpenPXE Easiest if the box is in front of you: 1. Power on, hit `F12`, pick **Network boot**. 2. Without any binding configured, the client will land on the - PXEForge menu (Default / Installers / Tools / Gated Deployment). + OpenPXE menu (Default / Installers / Tools / Queued Deployment). 3. Don’t pick anything. On your laptop: ```bash curl -fsS http://10.0.0.5/api/clients | jq . @@ -196,7 +196,7 @@ Easiest if the box is in front of you: 4. The most-recent entry is your target. Copy its `mac`. From here on we call this MAC `TARGET_MAC` (e.g. `aa:bb:cc:dd:ee:ff`). -Hyphens vs colons, upper vs lower case — PXEForge normalizes both. +Hyphens vs colons, upper vs lower case — OpenPXE normalizes both. --- @@ -227,7 +227,7 @@ curl -fsS -X POST http://10.0.0.5/api/hosts \ }' | jq . ``` -The binding is persisted to `~/pxeforge/work/hosts.json` and survives +The binding is persisted to `~/openpxe/work/hosts.json` and survives container restart. ### Confirm @@ -262,7 +262,7 @@ In order, with timing: | iPXE banner | ~1 s | The blue iPXE splash, version string | | iPXE second-stage DHCP | ~1 s | `Configuring (net0 …)` then `ok` | | HTTP boot script fetch | <1 s | `http://10.0.0.5/boot.ipxe?mac=…` | -| Per-MAC chain | <1 s | `PXEForge: per-MAC binding -> ubuntu-24-04-1-…` | +| Per-MAC chain | <1 s | `OpenPXE: per-MAC binding -> ubuntu-24-04-1-…` | | Kernel + initrd HTTP | 5–30 s | Two 200-OK fetches against `/iso//casper/vmlinuz` and `…/initrd` | | Kernel boot | 5–10 s | Kernel banner, then the Ubuntu/cloud-init splash | | Installer comes up | 30–60 s | The distro’s normal Live/installer environment | @@ -281,12 +281,12 @@ curl -N http://10.0.0.5/api/log/stream You’ll see each protocol step as it happens: ``` -INFO pxeforge::dhcp: reply mac=aa:bb:cc:dd:ee:ff arch=X8664Uefi target=tftp/snponly.efi -INFO pxeforge::tftp: RRQ snponly.efi blksize=1468 windowsize=8 → 982 KiB in 412 ms -INFO pxeforge::dhcp: reply mac=aa:bb:cc:dd:ee:ff (iPXE) target=http/boot.ipxe -INFO pxeforge::http: GET /boot.ipxe?mac=aa:bb:cc:dd:ee:ff → host binding hit -INFO pxeforge::http: GET /iso/ubuntu-…/casper/vmlinuz Range=bytes=0- 200 OK 14 MiB -INFO pxeforge::http: GET /iso/ubuntu-…/casper/initrd Range=bytes=0- 200 OK 75 MiB +INFO openpxe::dhcp: reply mac=aa:bb:cc:dd:ee:ff arch=X8664Uefi target=tftp/snponly.efi +INFO openpxe::tftp: RRQ snponly.efi blksize=1468 windowsize=8 → 982 KiB in 412 ms +INFO openpxe::dhcp: reply mac=aa:bb:cc:dd:ee:ff (iPXE) target=http/boot.ipxe +INFO openpxe::http: GET /boot.ipxe?mac=aa:bb:cc:dd:ee:ff → host binding hit +INFO openpxe::http: GET /iso/ubuntu-…/casper/vmlinuz Range=bytes=0- 200 OK 14 MiB +INFO openpxe::http: GET /iso/ubuntu-…/casper/initrd Range=bytes=0- 200 OK 75 MiB ``` The **Terminal** tab in the web UI shows the same thing live, plus a @@ -300,14 +300,14 @@ boots from your LAN, but the underlying ISO can come from anywhere your *host* can reach: - **Direct upload** from a remote workstation via the web UI (HTTPS - reverse-proxied if you put PXEForge behind nginx/Caddy). + reverse-proxied if you put OpenPXE behind nginx/Caddy). - **NFS mount** of a remote share — Sidebar → **Storage** → **NFS** → `nfs://files.lab.example.com/exports/isos`. Mounted ISOs show up in the same list and are PXE-bootable directly without copying. - **Pre-seed** from a CI job that `curl`s a vendor mirror and runs - `pxeforge seed --from`. + `openpxe seed --from`. -PXEForge itself never reaches out to the internet at boot time — all +OpenPXE itself never reaches out to the internet at boot time — all client traffic stays on the LAN, served from the host. --- @@ -337,21 +337,21 @@ curl -fsS -X POST http://10.0.0.5/api/hosts \ -d '{ "mac": "aa:bb:cc:dd:ee:ff", "target": "_local", "label": "lab-rack3-node07 (installed)" }' ``` -Now if anyone hits `F12 → Network` by accident, PXEForge replies +Now if anyone hits `F12 → Network` by accident, OpenPXE replies with a script that says *"chain back to local HDD"* and the box boots its real OS instead of re-imaging itself. This is the safest default for production hardware. --- -## 7. Re-imaging — the “Gated Deployment” flow +## 7. Re-imaging — the “Queued Deployment” flow Different scenario: you have **a rack of 30 servers** to image identically, all at once. Don’t bind 30 MACs by hand. Use the gate. 1. **Don’t** create host bindings. 2. PXE-boot every machine. They land on the menu. -3. On each: select **Gated Deployment**. They get position #1, #2, +3. On each: select **Queued Deployment**. They get position #1, #2, …, #30 and start long-polling. 4. In the UI: **Forge Gate** tab shows all 30 lined up. Pick the ISO, click **Assign to all waiting**. @@ -376,7 +376,7 @@ still in the kernel-fetch phase. | Release binding | `DELETE /api/hosts/` | | Live log | `curl -N http://$IP/api/log/stream` | | Prometheus metrics | `curl http://$IP/metrics` | -| Bulk import folder | `pxeforge seed --from /path` | +| Bulk import folder | `openpxe seed --from /path` | --- @@ -387,7 +387,7 @@ still in the kernel-fetch phase. `--network host`, check the host firewall on UDP 67/69/4011. - **iPXE shows `No more network devices`** — firmware NIC isn’t in PXE mode, or VLAN tagging is wrong. -- **iPXE prints `Connection timed out (http://…)`** — `PXEFORGE_PUBLIC_IP` +- **iPXE prints `Connection timed out (http://…)`** — `OPENPXE_PUBLIC_IP` is wrong. Clients can’t reach that IP. Check `/api/status` → `public_base_url` and `ping` it from the client subnet. - **Kernel panics during initrd load** — corrupt ISO upload. Check