Diskless HOWTO: network boot Linux with PXE, iPXE and NFS root

Updated November 2026

A diskless Linux machine loads its boot loader, kernel and initramfs over the network and mounts its root file system from a server. You need three services: DHCP to tell the client what to load, TFTP or HTTP to send the files, and NFS (or a disk image) for the root file system. The Linux Terminal Server Project (LTSP) sets all of this up for you on Debian and Ubuntu; this page also shows how to do it by hand.

The old Diskless HOWTO (2001) covered burning boot ROMs onto network cards. You no longer need to: almost every wired network card and UEFI firmware can network boot, and iPXE adds HTTP, scripting and menus on top.

When diskless makes sense

Diskless clients depend on the network and the server for every file they read. One server failure stops every client. If each machine must also work alone, install locally and automate it instead (see the KickStart HOWTO).

How network boot works

1. Firmware DHCP request 2. TFTP download iPXE 3. iPXE script kernel + initramfs 4. initramfs NFS root → systemd
iPXE runs a second DHCP request. The server recognizes it and hands out a script instead of iPXE again.
  1. The firmware's PXE code asks for an address. The DHCP reply includes a boot file name and the TFTP server address. BIOS clients need a BIOS boot program (undionly.kpxe); UEFI clients need an EFI program (ipxe.efi). The client reports its type in DHCP option 93 (client architecture).
  2. The client downloads iPXE and runs it. iPXE sends a new DHCP request with the user class iPXE.
  3. Seeing that user class, the server answers with an iPXE script. The script loads the kernel and initramfs, by TFTP or HTTP, and passes the kernel options.
  4. The initramfs brings up the network, mounts the root file system over NFS and starts systemd.

The quick way: LTSP

LTSP was rewritten in 2019 for systemd, UEFI and current desktops. It is packaged in Debian 13 (version 23.02, as of November 2026), and the upstream PPA carries the newest release for Ubuntu and Debian. It turns a normal installation (or a VM image or chroot) into a squashfs image, serves it over NFS or NBD, and gives each client a temporary writable layer in RAM. User home directories come from the server over SSHFS or NFS.

On a Debian or Ubuntu server with a desktop installed, as root:

apt install --install-recommends ltsp ipxe dnsmasq nfs-kernel-server openssh-server squashfs-tools ethtool net-tools
ltsp dnsmasq               # proxyDHCP: your router keeps handing out addresses
ltsp image /               # export the server's own system as the client image ("chrootless")
ltsp ipxe                  # generate the iPXE menu and copy iPXE into /srv/tftp
ltsp nfs                   # export the images over NFS
ltsp initrd                # pack users, groups and settings into /srv/tftp/ltsp/ltsp.img

If the server has a second network card just for the clients, give it the static address 192.168.67.1 and run ltsp dnsmasq --proxy-dhcp=0 instead, so the server hands out addresses itself. Run ltsp image / again after installing software or updates, and ltsp initrd after adding users or editing /etc/ltsp/ltsp.conf. (With the LTSP PPA, the package ltsp-binaries replaces ipxe.)

By hand: Debian 13 server, NFS root

This setup boots one or a few Debian 13 clients with a writable NFS root. It assumes the server has address 192.168.67.1 on interface enp2s0, which is a separate network with no other DHCP server.

1. DHCP and TFTP with dnsmasq

sudo apt install dnsmasq nfs-kernel-server debootstrap ipxe
sudo mkdir -p /srv/tftp
sudo cp /usr/lib/ipxe/undionly.kpxe /usr/lib/ipxe/ipxe.efi /srv/tftp/

The Debian ipxe package installs both boot programs in /usr/lib/ipxe/. Upstream builds are also on boot.ipxe.org (https://boot.ipxe.org/undionly.kpxe and https://boot.ipxe.org/x86_64-efi/ipxe.efi).

Create /etc/dnsmasq.d/netboot.conf:

interface=enp2s0
bind-interfaces
dhcp-range=192.168.67.100,192.168.67.200,12h
enable-tftp
tftp-root=/srv/tftp

# Tag requests that come from iPXE itself, and UEFI x86-64 firmware
dhcp-userclass=set:ipxe,iPXE
dhcp-match=set:efi64,option:client-arch,7
dhcp-match=set:efi64,option:client-arch,9

# First boot: hand out iPXE.  Second boot (from iPXE): hand out the script.
dhcp-boot=tag:!ipxe,tag:!efi64,undionly.kpxe
dhcp-boot=tag:!ipxe,tag:efi64,ipxe.efi
dhcp-boot=tag:ipxe,boot.ipxe
sudo systemctl restart dnsmasq
journalctl -u dnsmasq -f      # watch requests arrive

On a network that already has a DHCP server (for example your router), do not add a second one. Use dnsmasq's proxyDHCP mode (dhcp-range=192.168.1.0,proxy with pxe-service= lines) so it only supplies boot information, or let ltsp dnsmasq write that configuration for you.

2. Build the client's root file system

sudo debootstrap trixie /srv/nfsroot http://deb.debian.org/debian
sudo chroot /srv/nfsroot /bin/bash
# now inside the client system:
apt install linux-image-amd64 initramfs-tools nfs-common
sed -i 's/^BOOT=.*/BOOT=nfs/' /etc/initramfs-tools/initramfs.conf
update-initramfs -u -k all
echo diskless01 > /etc/hostname
passwd                        # set a root password, or add a user with sudo
exit

BOOT=nfs makes the Debian initramfs mount the root file system over NFS instead of from a local disk. Copy the kernel and initramfs to the TFTP directory:

sudo cp /srv/nfsroot/boot/vmlinuz-*    /srv/tftp/vmlinuz
sudo cp /srv/nfsroot/boot/initrd.img-* /srv/tftp/initrd.img

3. Export it over NFS

Add one line to /etc/exports and reload:

/srv/nfsroot  192.168.67.0/24(rw,no_root_squash,no_subtree_check)
sudo exportfs -ra
sudo exportfs -v

no_root_squash lets the client's root user act as root on these files, which the system needs. It also means anyone on that network can change them, so keep the boot network private.

4. The iPXE script

Create /srv/tftp/boot.ipxe:

#!ipxe
kernel vmlinuz root=/dev/nfs nfsroot=192.168.67.1:/srv/nfsroot ip=dhcp rw
initrd initrd.img
boot

File names in a script are relative to where the script came from, here the TFTP server. root=/dev/nfs is not a real device; it tells the kernel and initramfs to use NFS. nfsroot= gives server:/path, and ip=dhcp configures the network before the root file system is mounted. On Fedora or RHEL clients, which use dracut, the equivalent is root=nfs:192.168.67.1:/srv/nfsroot rw rd.neednet=1 ip=dhcp with the dracut nfs module in the initramfs.

Now set the client's firmware to boot from the network (often F12 at power-on) and watch it come up. Over HTTP instead of TFTP, large initramfs files load much faster: serve /srv/tftp with any web server and use http://192.168.67.1/… URLs in the script.

Many clients, one image

A single writable NFS root is fine for one machine. Several clients writing the same /var and /etc will corrupt each other's state. For many clients, export the image read-only and give each client a RAM overlay (what LTSP does), or give each client its own copy of the root directory and its own nfsroot= path, choosing the script per client with dhcp-host= tags in dnsmasq.

UEFI Secure Boot

An ordinary ipxe.efi build is not signed for Secure Boot, so firmware with Secure Boot on refuses to start it. Prefer the signed build over switching Secure Boot off: turning it off removes the check that stops a tampered boot loader or kernel from running, so do that only on a closed lab network you control. To use the signed build, download ipxeboot.tar.gz from the iPXE GitHub releases, hand out ipxeboot/x86_64-sb/ipxe-shim.efi as the boot file, and add a shim line to the script, before boot, that loads your distribution's signed shim (from its shim-signed package). The kernel must be one your distribution signed. The signed iPXE build has fewer features, and you cannot build your own variant of it.

Troubleshooting

The client says "No boot filename received" or "PXE-E53"
No DHCP reply carried a boot file. Check journalctl -u dnsmasq, the interface= line, and that no firewall blocks UDP 67 and 69 (and 4011 for proxyDHCP). sudo tcpdump -ni enp2s0 port 67 or port 69 shows the traffic.
iPXE loads iPXE again, forever
The second request was not recognized. Check the dhcp-userclass=set:ipxe,iPXE line and the order of the dhcp-boot lines.
"VFS: Unable to mount root fs" or the initramfs drops to a shell
Check that BOOT=nfs was set before update-initramfs, that the path in nfsroot= matches exportfs -v, and that the server allows NFS (with firewalld: services nfs, mountd and rpc-bind).
The client freezes a minute after boot
Something in the client re-configured or took down the network interface its root file system depends on. Do not let ifupdown, NetworkManager or systemd-networkd manage that interface on the client.

The classic Diskless HOWTO has been withdrawn from tldp.org for review. For NFS server options, see the NFS HOWTO; for the kernel options above, the BootPrompt HOWTO.

Sources