Diskless HOWTO: network boot Linux with PXE, iPXE and NFS root
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
- Classrooms and labs. Twenty or two hundred PCs boot the same image. You update one copy on the server, and a reboot gives every client the new version.
- Compute clusters and test rigs. Nodes boot a known image every time and keep nothing local.
- Kiosks and thin clients that should not store data.
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
- 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). - The client downloads iPXE and runs it. iPXE sends a new DHCP request with the user class
iPXE. - 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.
- 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, theinterface=line, and that no firewall blocks UDP 67 and 69 (and 4011 for proxyDHCP).sudo tcpdump -ni enp2s0 port 67 or port 69shows the traffic. - iPXE loads iPXE again, forever
- The second request was not recognized. Check the
dhcp-userclass=set:ipxe,iPXEline and the order of thedhcp-bootlines. - "VFS: Unable to mount root fs" or the initramfs drops to a shell
- Check that
BOOT=nfswas set beforeupdate-initramfs, that the path innfsroot=matchesexportfs -v, and that the server allows NFS (with firewalld: servicesnfs,mountdandrpc-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
- Linux Terminal Server Project and LTSP installation, ltsp.org
- LTSP Personal Package Archive and ltsp package tracker, Debian
- Chainloading iPXE, ISC dhcpd and iPXE, Scripting, kernel and initrd commands, ipxe.org
- UEFI Secure Boot, iPXE
- dnsmasq(8) man page
- Mounting the root filesystem via NFS (nfsroot), kernel.org
- initramfs.conf(5) and initramfs-tools(7), Debian
- dracut.cmdline(7), man7.org
- Netboot the server installer on amd64 (dnsmasq PXE example), Ubuntu Server documentation