From f0e76f8aff5dd567f19cd463dc67699e4ee5828b Mon Sep 17 00:00:00 2001 From: beatzaplenty Date: Mon, 27 Jul 2026 15:03:01 +1000 Subject: [PATCH 1/4] fix(pxe-boot): use pxe-service for proxy DHCP initial boot offers MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit dnsmasq 2.93 does not send proxy DHCP offers from dhcp-boot alone in proxy mode — pxe-service is required to trigger proxy offers for the initial boot. Replace the dhcp-boot-based arch detection with pxe-service entries for arch 0 (BIOS), 7 (EFI BC / OVMF), and 9 (EFI x86-64), then use dhcp-boot only for the iPXE second-boot HTTP chain (tag:ipxe clients). Add log-dhcp for post-deploy verification. Co-Authored-By: Claude Sonnet 4.6 --- modules/build-types/pxe-boot.nix | 25 ++++++++++++++----------- 1 file changed, 14 insertions(+), 11 deletions(-) diff --git a/modules/build-types/pxe-boot.nix b/modules/build-types/pxe-boot.nix index 16a43b5..f8961f8 100644 --- a/modules/build-types/pxe-boot.nix +++ b/modules/build-types/pxe-boot.nix @@ -427,18 +427,21 @@ in # Without this dnsmasq tries to bind port 53 which systemd-resolved # already owns, causing startup failure. port = 0; - dhcp-range = [ "192.168.2.0,proxy" ]; - dhcp-match = [ - "set:ipxe,175" - "set:efi64,option:client-arch,7" - "set:efi64,option:client-arch,9" - ]; + log-dhcp = true; + dhcp-range = "192.168.2.0,proxy"; + # Detect iPXE clients on the second DHCP round (after loading the + # iPXE binary) so we can serve the HTTP menu instead of TFTP. + dhcp-match = "set:ipxe,175"; dhcp-userclass = "set:ipxe,iPXE"; - dhcp-boot = [ - "tag:ipxe,tag:efi64,http://${vars.pxeServerIp}/boot.ipxe" - "tag:ipxe,http://${vars.pxeServerIp}/boot.ipxe" - "tag:efi64,ipxe.efi,,${vars.pxeServerIp}" - "undionly.kpxe,,${vars.pxeServerIp}" + # iPXE second boot: chain to HTTP boot menu. + dhcp-boot = "tag:ipxe,http://${vars.pxeServerIp}/boot.ipxe"; + # Initial boot: use pxe-service so dnsmasq sends proxy DHCP offers. + # dhcp-boot alone does not trigger proxy offers in dnsmasq 2.93. + # Arch 0 = BIOS, 7 = EFI BC (OVMF), 9 = EFI x86-64. + pxe-service = [ + "0,\"Network Boot\",undionly.kpxe,${vars.pxeServerIp}" + "7,\"Network Boot\",ipxe.efi,${vars.pxeServerIp}" + "9,\"Network Boot\",ipxe.efi,${vars.pxeServerIp}" ]; }; }; From 7e4b2d33fb59b4f6751925c2da664aa50afed564 Mon Sep 17 00:00:00 2001 From: beatzaplenty Date: Mon, 27 Jul 2026 15:12:12 +1000 Subject: [PATCH 2/4] fix(pxe-boot): open UDP 4011 for PXE boot service discovery After the proxy DHCP handshake, the UEFI client queries the PXE boot server on UDP port 4011 to get the actual boot filename. This port was not open in the NixOS firewall, so all four PXE discovery attempts from the test VM silently timed out. dnsmasq was bound to 0.0.0.0:4011 but never received the packets. Co-Authored-By: Claude Sonnet 4.6 --- modules/build-types/pxe-boot.nix | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/build-types/pxe-boot.nix b/modules/build-types/pxe-boot.nix index f8961f8..af4dc2a 100644 --- a/modules/build-types/pxe-boot.nix +++ b/modules/build-types/pxe-boot.nix @@ -447,5 +447,5 @@ in }; networking.firewall.allowedTCPPorts = [ vars.ports.pxeBootHttp ]; - networking.firewall.allowedUDPPorts = [ vars.ports.pxeBootTftp 67 ]; + networking.firewall.allowedUDPPorts = [ vars.ports.pxeBootTftp 67 4011 ]; } From 3748c86049e0a641a89b10240b61c1ac0b0b7211 Mon Sep 17 00:00:00 2001 From: beatzaplenty Date: Mon, 27 Jul 2026 15:40:54 +1000 Subject: [PATCH 3/4] fix(pxe-boot): use current pxe-boot IP (.247) in dnsmasq until Stage 5 renumber vars.pxeServerIp was already set to the post-renumber target (.223) but the pxe-boot CT is still at .247, so dnsmasq was advertising .223 as the TFTP server and the VM couldn't reach it. Update to .247 so PXE boot works now; the comment reminds us to flip it back to .223 when Stage 5 step 6 renumbers the CT. Co-Authored-By: Claude Sonnet 4.6 --- variables.nix | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/variables.nix b/variables.nix index a16d792..cedea9b 100644 --- a/variables.nix +++ b/variables.nix @@ -4,7 +4,7 @@ homeDomain = "sweet.home"; # base LAN domain for service subdomains (pve., docker.) tailnetDomain = "tail13f623.ts.net"; # Tailscale MagicDNS suffix lanCidr = "192.168.2.0/24"; # LAN subnet - pxeServerIp = "192.168.2.223"; # pxe-boot host's LAN IP + pxeServerIp = "192.168.2.247"; # pxe-boot host's LAN IP (update to .223 when Stage 5 step 6 renumbers the CT) pbsIp = "192.168.2.244"; # Proxmox Backup Server LAN IP domainControllerIp = "192.168.2.253"; # FreeIPA domain controller / primary DNS From 548c6f50419e85e79b7ee597c7130d0252228522 Mon Sep 17 00:00:00 2001 From: beatzaplenty Date: Mon, 27 Jul 2026 15:50:19 +1000 Subject: [PATCH 4/4] chore(pxe-boot): remove log-dhcp debug flag now that PXE boot is confirmed working Co-Authored-By: Claude Sonnet 4.6 --- modules/build-types/pxe-boot.nix | 1 - 1 file changed, 1 deletion(-) diff --git a/modules/build-types/pxe-boot.nix b/modules/build-types/pxe-boot.nix index af4dc2a..aeb50e1 100644 --- a/modules/build-types/pxe-boot.nix +++ b/modules/build-types/pxe-boot.nix @@ -427,7 +427,6 @@ in # Without this dnsmasq tries to bind port 53 which systemd-resolved # already owns, causing startup failure. port = 0; - log-dhcp = true; dhcp-range = "192.168.2.0,proxy"; # Detect iPXE clients on the second DHCP round (after loading the # iPXE binary) so we can serve the HTTP menu instead of TFTP.