Using PXE boot to install Debian Linux
I’m setting up a home lab, and I wanted to experiment with using PXE to install an operating system on my hosts over my local network. This article documents the steps I took to get PXE boot working with Debian 12 and ISC Kea DHCP server.
First, I’ll set up a TFTP server to host the files required to install the OS. Then, I’ll describe how I configured Kea, a relatively new DHCP server, to provide clients with the required options for PXE booting. Finally, I’ll briefly describe the problems I encountered and how I solved them. I hope this post helps anyone interested in doing the same.
Lab overview
For context, this a digram showing the relevant parts of my lab:
┌──────────────────┐ ┌───────────────────────┐ ┌───────────────────┐
│ Router │ │TFTP, DHCP, DNS Server │ │ Client │
│ 192.168.125.1 │ │ 192.168.125.5 │ │ 192.168.125.x │
│ │ │ │ │ │
│ Protectli V1410 │ │ Dell OptiPlex 3070 │ │ Intel NUC 10I7FNK │
│ OPNsense 24.7 │ │ Debian 12 │ │ │
└──────────────────┘ └───────────────────────┘ └───────────────────┘
The client machine is configured for EFI boot and will attempt to boot from the network if no OS is installed.
Setting up a TFTP server
PXE booting requires a TFTP server to host the boot image and installation media. When the PXE clients boot, the DHCP server will provide them with the TFTP server address and path where they can get the required files.
I’m using tftpd-hpa as the TFTP server in my lab. I installed it by running apt-get install tftpd-hpa. I added an option to its defaults file to increase the verbosity of logs, so my /etc/default/tftpd-hpa file looks like this:
# /etc/default/tftpd-hpa
TFTP_USERNAME="tftp"
TFTP_DIRECTORY="/srv/tftp"
TFTP_ADDRESS=":69"
TFTP_OPTIONS="-v --secure" # I added the -v option to get more verbose logs
Without the -v option, the tftpd-hpa logs didn’t show client requests. Logging the requests is helpful becuase it can make troubleshooting easier.
I restarted the service by running systemctl restart tftpd-hpa after updating the defaults.
TFTP is an old, simplistic, and insecure protocol, and it should *not* be exposed outside of your local network. See `man tftpd` for more information.
The directory /srv/tftp was created when the tftpd-hpa package was installed. This will be where my OS boot image and installation media will be stored. I used the below steps provided in the Debian wiki to get everything ready:
# export ARCH=amd64
# export DIST=stable
# export YOURMIRROR=deb.debian.org
# wget http://"$YOURMIRROR"/debian/dists/$DIST/main/installer-"$ARCH"/current/images/netboot/netboot.tar.gz
# sudo tar -xzf netboot.tar.gz -C /srv/tftp/
# cd /srv/tftp/
# ln -s debian-installer/amd64/grubx64.efi .
# ln -s debian-installer/amd64/grub .
Once the server was installed and the files ready, I confirmed that the service was running and tested that it was working as expected by getting a file from my workstation:
# confirm that the service is running
$ systemctl status tftpd-hpa.service
● tftpd-hpa.service - LSB: HPA's tftp server
Loaded: loaded (/etc/init.d/tftpd-hpa; generated)
Active: active (running) since Thu 2025-01-02 12:09:06 CST; 2 weeks 4 days>
...
# test getting a file from my workstation (IP addr 192.168.125.101)
$ tftp 192.168.125.5
tftp> get version.info
Received 65 bytes during 0.0 seconds in 1 blocks
# view the tftpd-hpa service logs
$ sudo journalctl -u tftpd-hpa -l --no-pager
Jan 22 10:37:23 mgmt1 in.tftpd[1103485]: RRQ from 192.168.125.101 filename version.info
The PXE boot image and OS installation media is now ready for my clients to use.
Configuring Kea DHCP service
The DHCP server must provide the correct options to the PXE clients for them to boot successfully. These options direct PXE clients to the TFTP server that was set up in the previous section so they can load the installer.
Most of the documentation I found shows how to configure a popular older DHCP server, ISC DHCP. ISC DHCP is no longer maintained, so I’m using Kea in my lab, which is the new DHCP server from ISC that replaces the older one. Kea is configured differently. Installing and configuring Kea is beyond the scope of this article, but the relevant configuration settings I’m using are shown in the snippet below:
# /etc/kea/kea-dhcp4.conf
{
"Dhcp4": {
...
"client-classes": [
{
"name": "pxe_efi",
"test": "(option[93].hex == 0x0007) or (option[93].hex == 0x0009)", // 93 is client architecture type option
"next-server": "192.168.125.5",
"boot-file-name": "debian-installer/amd64/bootnetx64.efi"
},
{
"name": "pxe_legacy",
"test": "not((option[93].hex == 0x0007) or (option[93].hex == 0x0009))", // 93 is client architecture type option
"next-server": "192.168.125.5",
"boot-file-name": "pxelinux.0"
}
],
"option-data": [
...
],
"subnet4": [
{
"subnet": "192.168.125.0/24",
...
}
],
...
}
}
Clients provide DHCP option 93 to the server, which indicate their architecture type. This allows the DHCP server to direct the client to the appropriate boot image for its architecture. The logic in the test fields above checks if the client architecture is 7 or 9 and provides the EFI boot image. If not 7 or 9 (the client is not using EFI or is not Intel x86 architecture), then it should be provided with the path to the legacy image (I haven’t tested this with non-EFI clients in my lab yet).
Once I added the correct client classes to my Kea configuration file and restarted the Kea service (systemctl restart kea-dhcp4-server.service), I was ready to boot my first PXE client.
Booting the client
Once the files were set up on the TFTP server and Kea’s configuration was updated, I powered on the client. It required a few attempts to get everything working correctly. Below are the issues I encountered and how I solved them.
Issue 1: wrong architecture type
Based on my reading of the documentation, I initally assumed that 9 was the correct value for architecture type becuase the corresponding name is “EFI x86-64”. However, when I attempted to boot my client, I received the following error message because it was provided the legacy boot image instead of the EFI image.

I ran a quick packet capture on the DHCP server - tcpdump -n -i enp1s0 -w /tmp/dhcp.pcap port 67 and port 68. When I opened it in Wireshark, I found that the client was using 7 as its architecture type.

I solved this by updating the logic in the test field to check for 7 or 9, as shown in the snippet above.
Issue 2: server-hostname option doesn’t work as expected
My initial understanding was that the server-hostname field in the Kea configuration could be set instead of next-server. I wanted to use a hostname instead of an IP address in case the IP address of my TFTP server ever changes. However, I wasn’t able to get this to work - when I configured Kea with my TFTP server hostname in the server-hostname field and omitted next-server, I received the error message below, “Unable to fetch TFTP image: Invalid Parameter / start_image() returned Invalid Parameter” when booting my client.

When I removed server-hostname and added next-server, as shown in Kea configuration snippet above, the client was able to get the boot image.
Issue 3: missing firmware
Later, after some initial testing, I ran into an issue where the firmware required for the Intel wireless NIC in my client was not included in the boot image.

Following the steps below from the Debian wiki, I was able to make the required firmware available to the installer:
# cd /srv/tftp/debian-installer/amd64
# [ -f initrd.gz.orig ] || cp -p initrd.gz initrd.gz.orig
# [ -f firmware.cpio.gz ] || wget https://cdimage.debian.org/cdimage/firmware/bookworm/current/firmware.cpio.gz
# cat initrd.gz.orig firmware.cpio.gz > initrd.gz
You may not need to do this depending on your hardware.
Success
Once Kea was configured to check for the right architecture type option values, the non-free firmware was included in the boot image, and the next server option was correctly set, the client loaded the installer and was able to install Debian 12 successfully!

Next steps
This was the first experience I’ve had with installing Linux over the network. It’s nice not having to keep physical installation media around, but this method still required answering the installer prompts manually. Debian provides a way to automate the installation process that I want to check out. I plan on experimenting with it in my lab and documenting the experience in subsequent posts.