Andrew Mercer
on this page

PXE (Preboot Execution Environment) lets a machine boot over the network with no physical install media: firmware requests a DHCP lease, gets pointed at a TFTP server, downloads a small bootloader, and that bootloader fetches a kernel/initrd (and, for Kickstart/Autoinstall/Preseed, the answer file) over TFTP or HTTP. It is the delivery mechanism the other pages in this section usually assume once you move past a single one-off VM.

What's required

  1. DHCP that hands out next-server (the TFTP server IP) and filename (the bootloader path) alongside the normal lease, or a separate DHCP proxy if you can't touch the main DHCP server's config
  2. TFTP serving the bootloader and kernel/initrd
  3. HTTP (usually) serving the actual kickstart/preseed/autoinstall file and the install tree, since TFTP is slow and stateless for anything beyond the initial small files

A minimal all-in-one server with dnsmasq

dnsmasq can act as DHCP-proxy (not replacing an existing DHCP server) plus TFTP plus a small HTTP helper, which is the easiest way to stand up netboot without touching a production DHCP server's config:

# /etc/dnsmasq.d/pxe.conf
interface=eth0
dhcp-range=192.0.2.0,proxy          # proxy mode: piggybacks on existing DHCP, only adds boot info
dhcp-boot=pxelinux.0
enable-tftp
tftp-root=/srv/tftp

# For UEFI clients, serve a different bootloader based on the client arch (option 93)
dhcp-match=set:efi-x86_64,option:client-arch,7
dhcp-boot=tag:efi-x86_64,bootx64.efi
mkdir -p /srv/tftp/pxelinux.cfg
cp /usr/lib/syslinux/pxelinux.0 /srv/tftp/
cp /usr/lib/syslinux/modules/bios/{ldlinux,libcom32,libutil,menu,vesamenu}.c32 /srv/tftp/
# /srv/tftp/pxelinux.cfg/default
DEFAULT rhel9
LABEL rhel9
  KERNEL rhel9/vmlinuz
  APPEND initrd=rhel9/initrd.img inst.ks=http://<server>/kickstart/server01.cfg ip=dhcp

LABEL ubuntu-autoinstall
  KERNEL ubuntu/vmlinuz
  APPEND initrd=ubuntu/initrd autoinstall ds=nocloud-net;s=http://<server>/autoinstall/

Copy the distro's vmlinuz/initrd.img from the install ISO into matching subdirectories under /srv/tftp/, and serve kickstart/, autoinstall/, or preseed.cfg over a plain web server (nginx/Apache, or even python3 -m http.server for quick testing).

iPXE for HTTP-chainloading and flexibility

iPXE replaces the legacy PXE ROM/pxelinux.0 chain with a scriptable bootloader that can itself fetch its next stage over HTTP (faster and easier to firewall than raw TFTP), and supports conditionals — e.g. choosing an image by MAC address:

#!ipxe
:menu
menu Choose an OS
item rhel9   RHEL 9 (Kickstart)
item ubuntu  Ubuntu Server (Autoinstall)
choose target && goto ${target}

:rhel9
kernel http://<server>/rhel9/vmlinuz inst.ks=http://<server>/kickstart/server01.cfg ip=dhcp
initrd http://<server>/rhel9/initrd.img
boot

:ubuntu
kernel http://<server>/ubuntu/vmlinuz autoinstall ds=nocloud-net;s=http://<server>/autoinstall/
initrd http://<server>/ubuntu/initrd
boot

Point dhcp-boot at ipxe.efi/undionly.kpxe instead of pxelinux.0/bootx64.efi to chainload into this instead.

netboot.xyz

netboot.xyz is a ready-made iPXE menu server covering most common distros' installers, useful for a lab/homelab where maintaining your own kernel/initrd mirror for every distro isn't worth it — point dhcp-boot at their hosted (or self-hosted, via their Docker image) boot menu and it handles the rest, though for anything beyond a stock install you'll still supply your own kickstart/autoinstall/preseed URL as a kernel parameter as shown above.

Where Cobbler/Foreman fit in

Cobbler and Foreman automate everything on this page — DHCP reservations, TFTP file layout, PXE menu generation, per-host kickstart/preseed rendering — behind a CLI/web UI, so you stop hand-editing pxelinux.cfg entries and dnsmasq.conf for every new host or distro version. Worth adopting once you're maintaining netboot for more than a handful of machines by hand.