bty via netboot (bty-web container deploy)¶
The network-flash flow needs a long-running bty-web: it serves the browser
UI, the per-MAC PXE plans, the iPXE bootfiles, and the image bytes. The
operator’s existing LAN DHCP server points PXE clients at this host; targets
PXE-boot into the server’s catalog, flash themselves, reboot.
Run bty-web as a container. The deploy ships three pieces:
bty-web (
ghcr.io/safl/bty-web) – the policy / PXE layer: UI, PXE plans, boot artifacts, and images over HTTP on:8080.withcache (
ghcr.io/safl/withcache) – a URL-keyed artifact cache that holds the image bytes, so a fleet pulls each image once.tftp (
ghcr.io/safl/bty-tftp, optional) – a small TFTP sidecar that serves the ~1 MB iPXE bootfile for legacy BIOS / older UEFI clients that bootstrap over TFTP. It serves only the bootfile; the kernel / initrd / squashfs come from bty-web over HTTP.
The operator UI is gated by $BTY_ADMIN_PASSWORD (unset = open, with a startup
warning). End state after up: a browser URL ready to register machines and
serve images.
Set it up¶
sudo uvx bty-lab deploy /opt/bty
# bty: http://<host>:8080/ui (login: bty-lab / bty-lab)
# withcache: http://<host>:3000/ (login: bty-lab / bty-lab)
That’s the full bring-up. The remainder of this walkthrough – DHCP
wiring, the PXE flash flow, known limitations – is what you do once the
server is running. For the deploy mechanics (bind-mount layout,
--systemd Quadlet install, upgrade semantics, password rotation), see
deploy/README.md
and walkthrough-server-docker.md.
Configure DHCP¶
bty runs no DHCP role: a working LAN DHCP server is a hard prerequisite, and its config carries the PXE / HTTP-Boot pointers (option 60 / 66 / 67) that direct clients at this host. The exact values for your deploy are on the bty-web Settings page under DHCP / Network boot.
PXE (TFTP) |
UEFI HTTP Boot |
|
|---|---|---|
Vendor class (option 60) |
|
|
Next-server (option 66) |
host IP |
host IP (still required) |
Bootfile (option 67) |
|
|
Option 66 stays pointed at the host for HTTP Boot too: bty’s iPXE binary chains
on to http://<host>:8080/pxe-bootstrap.ipxe, so it needs the next-server even
though the bootfile is a full URL. Once iPXE is running, both paths fetch
/pxe-bootstrap.ipxe, then the per-MAC plan.
Flash a target over PXE¶
Once a target’s MAC is registered with an assigned image, set the target’s
BIOS / UEFI to boot from the network (PXE) first. bty then drives every
subsequent boot via boot_mode; you set the firmware order once. Mind the
post-flash boot: with boot_mode=ipxe-exit (the default) bty boots the disk
via iPXE – UEFI exits to the firmware boot order, legacy BIOS sanboots the
drive – and the bty-flash-* modes boot the just-flashed disk the same way.
On legacy BIOS the drive is 0x80 (first disk) unless you set sanboot_drive;
on UEFI there’s nothing to set. (A flashed box that won’t boot is almost always
a firmware / drive-number problem; see
Firmware boot order.) On power-on it will:
DHCP-discover from your LAN’s DHCP server, configured to return option 66/67 pointing at the bty host.
Chain into the bty iPXE script. Cmdline carries
bty.server=URL+bty.mac=MAConly.Boot the netboot kernel + initrd + squashfs trio.
bty-on-tty1.serviceexec’sbty --server X --mac Yon tty1;btyGETs<server>/pxe/<mac>/plan, seesmode=flash(because boot_mode=bty-flash-always + ref + serial), writes the image to the local disk, POSTs/pxe/<mac>/done, reboots.
The server’s machine-detail page shows live progress + last flashed timestamp.
What happens on subsequent boots depends on the boot_mode you picked:
bty-flash-once(the default for one-shot CI reflashes): the next boot still hits bty’s iPXE chain, sees the bound flash has already happened, and servesipxe-exitso the firmware falls through to the freshly-flashed disk.bty-flash-always(per-job CI cadence): the iPXE chain alternates flash/sanboot per/pxeGET. The PXE-first firmware order stays in effect; the alternation gives a freshly-flashed boot in between every flash so the operator’s job runs on the intended image and the next job starts from a clean reflash.
Either way, the freshly-flashed image runs whatever it provisions to.
What you can do today¶
PXE-flash any number of targets to a registered image, hands-free, in parallel.
Mix the network-flash flow (this walkthrough) with the USB-stick flow (bty via bty-usb): both run the same
btyflash code, driven by the plan endpoint vs the local wizard.Swap images per-target without rebooting the server.
Known limitations¶
DHCP stays with the operator’s LAN. bty runs no DHCP server (proxy or full); a working LAN DHCP server is a hard prerequisite, and its config must be extended with option 60 / 66 / 67 to direct PXE clients at the bty host.
UEFI Secure Boot isn’t supported: the bty netboot kernel isn’t shim-signed. Disable Secure Boot on targets you’re PXE-flashing, or use the USB stick flow.
Single bootfile, no DHCP userclass logic (UEFI). Stock iPXE re-DHCPs after it loads and re-fetches the DHCP bootfile – itself – unless the DHCP server hands iPXE a different bootfile by matching
user-class=iPXE. bty sidesteps that: its customipxe.efi(baked into the bty-web and bty-tftp images) embedschain http://${next-server}:8080/pxe-bootstrap.ipxe, so the operator’s DHCP only ever needs one bootfile and no userclass rules. The legacy-BIOSundionly.kpxeis stock, so BIOS still needs the userclass trick (UniFi / Kea client-classes, dnsmasqdhcp-userclass, ISC-DHCPd conditionalif) – and BIOS is unverified anyway (below).UEFI tested, legacy BIOS not yet. bty’s netboot path has so far been exercised only on UEFI targets. The legacy-BIOS branch (
sanboot --driveinstead of the UEFI hand-back to firmware) is implemented but not field-tested; treat BIOS as unverified for now.