Back to blog
FILE 0x1B·THE BOOT LOADER THAT PHONED HOME

The boot loader that phoned home

September 1, 2026 · homelab, debugging, pxe, networking

My network boot server has a habit: every time I actually need it, it's broken. Not down — broken in a specific, boring way that takes twenty minutes to rediscover. This time I stopped fixing the symptom and went looking for why it kept coming back.

The complaint was "it isn't authenticating." That's an odd thing for a PXE server to do, since nothing in the chain asks for a password.

What the logs actually said

Stage one was flawless. The client's firmware broadcast a DHCP discover, the proxyDHCP responded, and TFTP handed over the iPXE binary:

PXE(eth0) 00:e0:4c:xx:xx:xx proxy
tags: efi64, eth0
bootfile name: ipxe.efi
...
dnsmasq-tftp: sent /config/menus/ipxe.efi to 10.0.0.109

And then nothing. No HTTP request for the boot menu. Not a failed request — no request at all. Whatever iPXE did after it loaded, it wasn't talking to my server.

The two-stage trick, and its load-bearing assumption

A PXE chainload has two stages that look identical from the outside and are completely different underneath:

  1. The NIC firmware asks DHCP for a boot file, gets ipxe.efi, TFTPs it, runs it.
  2. iPXE itself now asks DHCP the same question — and if it gets the same answer, it downloads and runs itself again, forever.

Everyone solves stage two the same way: tag the second request and answer it differently. iPXE announces itself with user-class iPXE and DHCP option 175, so dnsmasq can match on that and hand back an HTTP menu URL instead of a binary:

dhcp-match=set:ipxe,175
dhcp-userclass=set:ipxe,iPXE
pxe-service=tag:ipxe,x86-64_EFI,"local menu",http://10.0.0.5/menu.ipxe

That works. It also means the health of your boot server depends on a tag match inside a second DHCP conversation you never see, on a network where a router firmware update can quietly wipe the options out from under you.

Where "authentication" came from

Here's the part I'd missed for months. The binary I was serving was the stock upstream release, and stock iPXE builds ship with a chain URL compiled in: https://boot.netboot.xyz/menu.ipxe.

So when the stage-two tag match failed, iPXE didn't error out. It fell back to its built-in URL, went out to the internet over TLS, failed to validate the certificate chain, and printed:

Permission denied (https://ipxe.org/0216...)

Permission denied. To a person standing at a machine that won't boot, that reads exactly like an authentication failure — which is why every previous debugging session started in the wrong place. The message described the fallback path, not the path I'd configured.

The fix: stop asking

The real problem isn't the tag match. It's that the boot loader had an opinion about where the menu lives, and my DHCP server had to argue it out of that opinion on every boot. Remove the argument: compile the answer into the binary.

$ cat embed.ipxe
#!ipxe
set attempt:int32 0
:top
inc attempt
dhcp || goto retry
chain --autofree http://10.0.0.5/menu.ipxe || goto retry
exit 0
:retry
iseq ${attempt} 4 && goto giveup ||
sleep 3
goto top
:giveup
echo Could not reach the local boot menu - dropping to iPXE shell
shell

$ git clone --depth 1 https://github.com/ipxe/ipxe
$ cd ipxe/src
$ make -j4 EMBED=/path/to/embed.ipxe \
       bin-x86_64-efi/ipxe.efi bin-i386-efi/ipxe.efi bin/undionly.kpxe

Ten minutes of build time. Now iPXE still does DHCP — it needs an address — but it no longer asks where to go. There is no fallback to the public internet, no TLS, no certificate chain, and nothing for a router firmware update to break. Worst case it can't reach my menu and drops to a shell that says so, instead of producing a misleading error about permissions.

Two things I'd have done sooner:

bash curl -s --max-time 20 -o "$T" tftp://10.0.0.5/ipxe.efi || fail "TFTP fetch failed" grep -qa "10.0.0.5/menu.ipxe" "$T" || fail "binary has no embedded local menu URL"

"Is the service running" and "does the service do the thing" are different questions, and only one of them was ever going to catch a boot loader that phoned home.