I just want to PXE-boot my PC with some image on a Debian-based server.

This should be the least-hassle method of booting a PC over the network from a Debian-based server using the Intel PXE interface.

I set this up to PXE-boot memtest86+, but anything that can be loaded via the PXELINUX loader (such as a Linux kernel) can be added to the menu and booted this way. Building the menu via PXELINUX avoids the byzantine and reportedly bug-ridden PXE menu facility.

Minumum Viable TFTP Server Configuration

Install and enable a TFTP server:

# apt-get install openbsd-inetd tftpd
# update-inetd --enable tftp

(or edit /etc/inetd.conf)

# killall -HUP inetd

Create the file hierarchy to boot via TFTP (menu config taken from here):

# mkdir -p /srv/tftpboot/pxelinux.cfg
# chmod a+rx /srv /srv/tftpboot /srv/tftpboot/pxelinux.cfg
# apt-get install syslinux-common
# ln -s /usr/lib/syslinux/pxeboot.0 /srv/tftpboot
# ln -s /usr/lib/syslinux/menu.c32 /srv/tftpboot
# cat > /srv/tftpboot/pxelinux.cfg/default
MENU TITLE PXEBoot Server.  Use at own risk.
LABEL local
      MENU LABEL (local)
MENU end

Then verify:

# find /srv/tftpboot

Minimum Viable DHCP Boot Server Configuration

I’m using the ISC DHCP server version 3. The DHCP server should have at least this configuration in /etc/dhcp/dhcpd.conf for the subnet the booting PC is on, although you can safely adapt it to your needs.


  • Client is Intel PXE ROM
  • Network is
  • DHCP server is
  • TFTP server is the same machine,
  • TFTP server is rooted at /srv/tftp
  • We are booting the PXELINUX kernel (pxelinux.0)
option space PXE;
option PXE.discovery-control code 6 = unsigned integer 8;
option PXE.boot-server code 8 = { unsigned integer 16,
                                  unsigned integer 8,
                                  ip-address };
option PXE.boot-menu code 9 = { unsigned integer 16,
                                unsigned integer 8,
option PXE.menu-prompt code 10 = { unsigned integer 8, text };

shared-network "workstations" {
  subnet netmask {
  option subnet-mask;
  option broadcast-address;
  default-lease-time 1200;
  max-lease-time 7200;

  allow booting;

  # Order of parameter request list is important!
  # For PXE as early as 1.0, 60,43,67 are required; must be in that order; and must be first in the list.
  option dhcp-parameter-request-list 60,43,67;
  # A list that includes PXE as well as "normal" DHCP parameters such as DNS servers, gateway, etc:
  #option dhcp-parameter-request-list 60,43,67,1,3,6,12,15,17,26,28,40,119;

  # Configure the PXE DHCP options now:
  # Option 60
  option vendor-class-identifier "PXEClient";
  # Option 43
  vendor-option-space PXE;
  option PXE.discovery-control 15;  # Disable menus and server discovery; use provided file only.
  option PXE.boot-server 15 1;
  # Option 67
  option bootfile-name "/srv/tftp/pxelinux.0";

  # Other netboot-related parameters that can be played with if necessary:
  # Option 66 - unused by PXE
  #option tftp-server-name "";

  # BOOTP parameters - unused by PXE
  #allow bootp;
  #server-name "";
  #filename "/srv/tftp/pxelinux.0";

After adding/editing the dhcpd.conf, restart the DHCP server:
# service isc-dhcp-server restart

You should now be able to boot into the PXELINUX menu. From here, you can add menu options such as the aforementioned memtest86 (note: PXE cannot boot a gzip file).


Dang, it didn’t work! Well, aside from googling the various opaque PXE error messages, there are a few things you can try:

Check logs

/var/log/dhcpd.log and /var/log/syslog may have useful information from the DHCP server and TFTP server (respectively).


dhcpdump will parse DHCP/BOOTP messages into human-readable format.


Try to strace -f the inetd process as it launches the TFTP server to see what’s going on.


tcpdump -A -n -i <interface> udp
This will give you ASCII dumps of all UDP packets, which will be helpful if the client is doing strange things. For example, attempting to contact a non-existent “proxyDHCP” server on port 4011, or making an unexpected file request. Example:

08:22:01.271402 IP >  42 RRQ "/srv/tftp/pxelinux.0^A^DM-^?M-^?M-^?" octet tsize 0

…uh, what?

PXE ROM bugs

The Intel Pro/100 NIC I was using shows the following banner at boot:

Intel UNDI, PXE-1.0 (build Dev)
Copyright (C) 1997,1998 Intel Corporation

PXE 1.0 is a very old version, and has at least one bug: in this setup at least, it requests the boot filename the DHCP server provided it, but with trailing garbage. Of course, a boot file named with trailing garbage is never found on the TFTP server, and so the boot fails.

How to fix it? I wrote a tiny C program to create a symbolic link from the real boot filename to the garbled request filename, which I determined with precision using strace on the TFTP server.

#include <stdio.h>

int main(void) {
  return symlink("pxelinux.0", "pxelinux.0\001\004\377\377\377");

A general solution appears elusive.

One Response to “I just want to PXE-boot my PC with some image on a Debian-based server.”

  1. DOS says:

    Thanks for providing the obscure settings needed for my Pro/100 or Pro/100+! I found that when I used ‘option bootfile-name’ exactly as in your example (not expecting it to work due to my having a different path), I seemed to get TFTP requests with the same trailing garbage, but when I removed that line entirely falling back to what I already had in my configuration:

    filename “/pxelinux.0”;

    it worked just fine without any trailing garbage appearing. Perhaps my shorter path due to passing ‘-s’ to tftpd (secure mode – chroot on startup) avoids some bug in PXE-1.0; perhaps PXE-1.0 doesn’t properly handle filenames above a certain length.

Leave a Reply