How to set up a TFTP server on Ubuntu for network boot

Network boot gives a client a small, dependable way to fetch startup files across the local network. The Trivial File Transfer Protocol, or TFTP, is deliberately simple: it transfers files using UDP and uses port 69 for the initial request. PXE, short for Preboot Execution Environment, builds on this service by allowing DHCP to tell the client which server and boot file to request.

On Australian home connections, the process stays inside the router even when internet access comes over NBN or fixed wireless. Many households use the modem/router supplied by Telstra, Optus, iiNet or Aussie Broadband, or replace it with their own unit. Those interfaces vary, but the relevant controls are normally labelled TFTP server, next server, boot server or PXE, and they may sit in different menus.

Local address ranges are not standardised: a box in Melbourne, Brisbane, Perth or Sydney may hand out addresses in the 192.168.1.x range, while another uses 10.0.0.x. Do not copy an example address blindly. A DHCP reservation for the Ubuntu machine’s MAC address is safer than guessing, particularly when a BYO router uses dynamic leases. NBN carrier-grade NAT also means there is usually no public address available for forwarding.

Before diving in, have a wired Ubuntu host, a client with PXE-capable firmware, the correct target kernel and initial RAM disk, and permission to change DHCP settings. A spare test machine on the local workbench is ideal. TFTP has no authentication or encryption, so treat it as a trusted-LAN tool rather than something to publish to the internet. On a long arvo, that limit is worth checking before anything else.

Understand the PXE and TFTP handshake

The TFTP server and the DHCP server perform different jobs. TFTP only delivers files. DHCP supplies the client’s temporary IP address and, for network boot, option 66 for the boot server and option 67 for the boot filename. DHCP and TFTP can live on different machines, although running both on one Ubuntu host is convenient.

A typical PXE exchange begins when the client broadcasts a DHCP request. The DHCP server selects an address and returns the server address and file path. The firmware then sends a TFTP read request, receives the initial boot loader, and loads a boot menu, kernel and initrd. A failure at any link can resemble a general networking fault, so understanding the sequence saves guesswork.

Use private LAN addresses throughout. For example, the router could be 192.168.1.1, dnsmasq could be 192.168.1.10, and the TFTP host could be 192.168.1.50. The DHCP server’s address and the next-server value need not match, but both must be reachable from the client. Public IPv4, IPv6 and internet port forwarding do not belong in this design.

Prepare the Ubuntu host and network

Use a supported Ubuntu Server or Desktop release with a stable wired interface. Find the interface name, current IPv4 address and default route before configuring anything:

ip -br link
ip -4 addr
ip route

Give the server a stable LAN address. The simplest approach is to create a DHCP reservation in the home router using the Ubuntu host’s MAC address, then renew its lease. A static Netplan address also works, but it must match the chosen subnet and avoid addresses already assigned by the router’s DHCP pool. Keep the server wired where possible because PXE transfers are sensitive to Wi-Fi latency and signal loss.

Install the TFTP daemon and a separate command-line client for testing. The package name is normally tftpd-hpa on current Ubuntu releases:

sudo apt update
sudo apt install tftpd-hpa tftp
sudo install -d -o tftp -g tftp -m 0755 /var/lib/tftp

The tftp package supplies the client used later to confirm that files can be retrieved. The daemon itself serves the /var/lib/tftp directory, and ownership by the unprivileged tftp account prevents the service from running with unnecessary root privileges.

Configure and start the TFTP daemon

Edit /etc/default/tftpd-hpa and replace the example address with the Ubuntu host’s real LAN IP:

sudo nano /etc/default/tftpd-hpa
TFTP_USERNAME="tftp"
TFTP_DIRECTORY="/var/lib/tftp"
TFTP_ADDRESS="192.168.1.50"
TFTP_OPTIONS="--secure"

Binding the daemon to one interface prevents it from listening on every available network. The --secure option confines clients to the configured directory and blocks common unsafe behaviour. If the server must listen on several trusted interfaces, use a deliberate address for each configuration rather than returning to an unrestricted bind address.

Start the service and confirm that UDP port 69 is available:

sudo systemctl enable tftpd-hpa
sudo systemctl restart tftpd-hpa
sudo systemctl status tftpd-hpa --no-pager
sudo ss -lunp | grep ':69'

If the service will not start, inspect recent messages with journalctl -u tftpd-hpa -n 50. Common causes include an unavailable address, an incorrect directory or restrictive AppArmor permissions. Ensure boot files remain readable by tftp; after copying new files, sudo chown -R tftp:tftp /var/lib/tftp and normal read permissions will prevent avoidable access errors.

Deliver the boot path through DHCP

If the home router supports PXE, create a DHCP reservation for the Ubuntu host and enter the TFTP server’s address in the next-server or boot-server field. For a BIOS PXELINUX setup, set the boot filename to pxelinux/pxelinux.0. This path is relative to /var/lib/tftp, and the client appends it to the server address supplied through DHCP option 66.

Some Australian routers provide these controls while others expose only DHCP reservations. In that case, dnsmasq can provide the required options from Ubuntu. First identify the wired interface and subnet, then create a dedicated configuration file:

sudo apt install dnsmasq
sudo nano /etc/dnsmasq.d/pxe.conf
interface=enp3s0
bind-dynamic
dhcp-range=192.168.1.100,192.168.1.199,255.255.255.0,12h
dhcp-option=3,192.168.1.1
dhcp-option=66,192.168.1.50
dhcp-option=67,pxelinux/pxelinux.0

Restart dnsmasq with sudo systemctl restart dnsmasq. Do not allow the router and dnsmasq to issue competing DHCP offers across the same client range. Move the clients to dnsmasq after confirming its configuration, or restrict dnsmasq to a test VLAN. Existing clients must renew their leases or reboot before they will request the new boot information.

Test file delivery and perform a network boot

Install PXELINUX and copy its initial boot image into the TFTP tree:

sudo apt install pxelinux syslinux-common
sudo install -d -o tftp -g tftp -m 0755 /var/lib/tftp/pxelinux
sudo cp /usr/lib/PXELINUX/pxelinux.0 /var/lib/tftp/pxelinux/

Create /var/lib/tftp/pxelinux/default with a simple menu, then place a matching kernel and initrd in the same directory:

DEFAULT ubuntu
PROMPT 0
TIMEOUT 50
LABEL ubuntu
  KERNEL vmlinuz
  INITRD initrd.img

The vmlinuz and initrd.img files must match the target computer’s architecture and boot method. They can come from a prepared netboot image or another trusted source. Copying mismatched files may produce a boot menu successfully but fail when the kernel starts.

From another Ubuntu or Linux computer on the same LAN, test the transfer directly:

tftp 192.168.1.50
get pxelinux/pxelinux.0 pxelinux.0
quit

Compare the downloaded file’s checksum with the server copy. If the request fails, capture traffic with sudo tcpdump -ni enp3s0 udp port 69 on the Ubuntu server. Then open the client’s one-time boot menu and select Network Boot or PXE. BIOS firmware can commonly load pxelinux.0, while UEFI normally needs an architecture-specific file such as bootx64.efi or efi/boot/bootx64.efi. Do not point UEFI directly at the BIOS loader; if the firmware offers only HTTP Boot, enable its PXE IPv4 option or use an iPXE-based boot path.

Secure, monitor and maintain the server

TFTP is unencrypted and unauthenticated, so keep it on a trusted LAN and review the UFW firewall guidance before enabling the service. A narrow initial-request rule can allow only the client subnet to reach the daemon:

sudo ufw allow from 192.168.1.0/24 to any port 69 proto udp

If dnsmasq supplies DHCP on the Ubuntu server, it also needs inbound UDP port 67 from the client subnet. Guest Wi-Fi networks often enable client isolation, which can block PXE even when ordinary browsing works, so use a wired segment or a non-isolated VLAN. Some TFTP implementations exchange data on dynamically allocated UDP ports; if the initial request appears in the logs but the transfer stalls, inspect the firewall log before adding any narrowly scoped data-port rule.

Keep the service enabled, monitor it with journalctl -u tftpd-hpa -f, apply Ubuntu security updates and back up the TFTP root, daemon configuration and DHCP settings. Never expose UDP port 69 through an NBN connection or public router, particularly because carrier-grade NAT may hide the attempt without making the service safe. The next concrete step is to run tftp 192.168.1.50, request pxelinux/pxelinux.0 and confirm that the received file matches the server copy.