Setting up a PXE boot environment – Part 1: TFTP Server & DHCP Server

Disclaimer: Regardless part 0 is more or less around Raspberry Pi, in this post, I am only implementing a bare-minimum version of PXE, it will only handle single architecture (x64) and either UEFI or BIOS, not a general purpose PXE environment. some of the PXE boot environment mentioned should also work with Raspberry Pi 3B+ or above, here’s the official Raspberry Pi documentation.

We need the following for this rudimentary homelab PXE boot setup:

  • A Linux system with command line tools: wget, tftp and nmap
  • TFTP server
  • DHCP server
  • HTTP server to serve ISO files over LAN – optional, but highly recommended. We’ll cover this in part 2.

Even I listed 3 “servers” above, think as 3 services, it could be provided by any number of servers, you could have your Linux do TFTP and HTTP, and your router for DHCP, or any other combination.

TFTP setup

Let’s start with the TFTP server. Huge thanks to Canonical folks, the Ubuntu wiki has the 23.04 netboot tarball, a quick tar -xf will yield all the files and folder structure we need:

 |-amd64
 | |-grubx64.efi
 | |-grub
 | | |-grub.cfg
 | |-pxelinux.0
 | |-linux
 | |-ldlinux.c32
 | |-initrd
 | |-bootx64.efi
 | |-pxelinux.cfg
 | | |-default

In the above folder structure, pxelinux.0 is for BIOS clients, while bootx64.efi is for UEFI clients, keep those filenames in mind, we’ll need those in configuring DHCP server in part 2.

There’s plenty TFTP server setup articles, Ubuntu, Raspberry Pi, Windows 10, or docker.

Once TFTP is ready, let’s use the following command to test TFTP, (replace the IP address to your TFTP server)

tftp 10.1.2.3

Then:

tftp> get grubx64.efi

If you see File not found error, that means your TFTP path is incorrectly configured.

tftp> get grubx64.efi
Error code 1: File not found

You should see something like this:

tftp> get grubx64.efi
Received 2302218 bytes in 0.7 seconds

Also try files in a folder:

tftp> get grub/grub.cfg
Received 943 bytes in 0.0 seconds

TFTP requires no login credential, I also found mixed results if the default tftp config allows file upload, it probably needs to be secured in some way, but I digress. Once it’s verified operational, take notes of your TFTP Server IP address.

DHCP setup

Next is the DHCP server. In short, we need option 66 for the TFTP server, and option 67 for the boot image filename. DHCP relay (aka IP helpers) or not, some DHCP server somewhere is gonna give you the TFTP server IP and the NBP (Network Boot Program) path.

The easiest method is to check your router, in a home network environment, most likely your router is already doing the DHCP work. If you have a unwieldy router that lacks the configuration, there’s a few routes (pun intended):

  • Setup your own DHCP server (and disable the DHCP server in the router)
  • Reflash the router firmware to OpenWRT and follow the PXEBoot instructions.
  • Get a proper router, such as OPNSense box, UniFi ecosystem, or something else.

First option, the most popular choices are isc-dhcp-server, or dnsmasq. No matter which you choose, it only works if it’s the only DHCP server in your network (i.e. you need to turn off the DHCP in your router). In the unfortunate event that your all-in-one box (modem + router + wifi) is supplied by your ISP and not configurable, you’ll need a separate router (and subnet) behind it. Worst case is to double-NAT the devices you intend to PXE boot. Otherwise, bridge mode, or IP passthrough might be possible, YMMV.

Second & third option would be best for existing modem + router/wifi setup – as there are separate devices. There’s more options as it involves how you want to setup your home network / homelab, it’s a deep rabbit hole and probably warrants a series of articles, but I digress…

Here’s the configuration in my UniFi router.

I don’t know why there’s “Network Boot” and “TFTP Server”, but you’ll need both of them to work.

Once done configuring DHCP, use the following command to test:

sudo nmap --script broadcast-dhcp-discover

You should see the following output:

Starting Nmap 7.80 ( https://nmap.org ) at 2023-10-11 23:13 PDT
Pre-scan script results:
| broadcast-dhcp-discover
|   Response 1 of 1:
|     IP Offered: 10.1.0.12
|     DHCP Message Type: DHCPOFFER
|     Server Identifier: 10.1.0.1
|     IP Address Lease Time: 2m00s
|     Bootfile Name: bootx64.efi\x00
|     Renewal Time Value: 1m00s
|     Rebinding Time Value: 1m45s
|     Subnet Mask: 255.255.0.0
|     Domain Name: local
|     Broadcast Address: 10.0.255.255
|     TFTP Server Name: 10.1.2.3\x00
|     Domain Name Server: 10.2.1.99, 10.2.1.98
|_    Router: 10.1.0.1

You might notice the \x00 at the end of TFTP server name and Bootfile Name, it is normal:

…the receiver of such options MUST be prepared to delete trailing nulls if they exist.

RFC 2132

In part 2, we’re gonna test PXE boot, and customize GRUB to our liking.