PXE Boot for Automated OS Installs

Khimananda Oli 8 min read Virtualization
PXE Boot for Automated OS Installs

By Khimananda Oli | Last reviewed: August 2026

Manually installing operating systems via USB drives is unscalable, error-prone, and incompatible with modern infrastructure demands. PXE Boot for Automated OS Installs solves this by enabling network-based provisioning that delivers identical, auditable configurations across your entire fleet. This guide covers building a production-grade PXE environment using dnsmasq and iPXE, integrating it with configuration management workflows discussed in my Ansible automation guide to ensure every node starts in a known-good state.

Bare Metal ClientPXE NIC / UEFIdnsmasq ServerDHCP + TFTP + DNSHTTP MirrorOS Images + KickstartConfig Mgmtcloud-init / Ansible1. DHCP Discover/Offer2. Fetch Kernel/Initrd3. User-data / Config
Core components of a PXE Boot for Automated OS Installs environment: client discovery, dnsmasq services, and HTTP-based artifact delivery.

How does PXE Boot for Automated OS Installs actually work?

PXE (Preboot Execution Environment) is not magic; it is a standardized sequence of network protocols that replaces local storage during the initial boot phase. Understanding this handshake prevents most troubleshooting headaches. When a server powers on with PXE enabled in firmware, the NIC broadcasts a DHCPDISCOVER packet with a specific option 60 tag identifying it as a PXE client.

Your DHCP server (typically dnsmasq in dedicated PXE setups) responds with an IP address plus two critical options: next-server (the TFTP server IP) and filename (the bootloader path). The client then contacts the TFTP server to download this bootloader—usually iPXE or GRUB—which contains logic to fetch the actual kernel and initramfs over HTTP. This separation matters: TFTP handles only the tiny initial payload because it lacks authentication and reliability features, while HTTP serves multi-gigabyte OS images efficiently.

In practice, I always use iPXE rather than legacy PXELINUX. iPXE supports HTTPS, iSCSI, FCoE, and scriptable menus, making it viable for complex environments where you need conditional logic based on hardware model or MAC address. Legacy PXELINUX is essentially dead in 2026; if you are starting fresh, skip it entirely.

How do you configure dnsmasq as a PXE server?

Dnsmasq is the standard choice for PXE infrastructure because it combines DHCP, TFTP, and DNS proxying in a single lightweight binary. On Ubuntu 24.04 LTS, installation is straightforward, but configuration requires precision to avoid conflicting with existing DHCP servers on your network.

sudo apt update && sudo apt install -y dnsmasq tftpd-hpa nginx

Create a dedicated configuration file at /etc/dnsmasq.d/pxe.conf. Never edit the main dnsmasq.conf directly; modular configs survive upgrades and simplify auditing.

# /etc/dnsmasq.d/pxe.conf
interface=eth1              # Dedicated provisioning NIC only
bind-interfaces             # Critical: prevents binding to all interfaces
dhcp-range=10.10.50.100,10.10.50.200,12h
dhcp-option=66,10.10.50.1   # next-server (TFTP IP)
dhcp-boot=ipxe.efi          # UEFI bootloader filename
enable-tftp
tftp-root=/var/lib/tftpboot
log-dhcp                    # Essential for debugging PXE handshakes
dhcp-match=set:bios,option:client-arch,0
dhcp-boot=tag:bios,undionly.kpxe
dhcp-match=set:efi64,option:client-arch,9
dhcp-boot=tag:efi64,ipxe.efi

The bind-interfaces directive is non-negotiable in multi-homed environments. Without it, dnsmasq may respond to DHCP requests on your production network, causing outages. I have seen this mistake take down entire datacenter segments during lab setup. Always verify with ss -ulnp | grep :67 that dnsmasq listens only on the intended interface.

For BIOS/legacy clients, you need undionly.kpxe; for UEFI, use ipxe.efi. Modern hardware is predominantly UEFI, but keeping both ensures compatibility with older inventory. Test both paths before declaring production readiness.

How do you create iPXE boot menus for unattended installs?

iPXE scripts transform PXE from a simple loader into a programmable provisioning platform. Place your menu at /var/lib/tftpboot/boot.ipxe and reference it from the embedded iPXE binary or chainload it from the initial DHCP filename.

#!ipxe
# /var/lib/tftpboot/boot.ipxe

menu PXE Boot for Automated OS Installs
item --gap --- Available Distributions ---
item ubuntu2404    Ubuntu 24.04 LTS Server (Auto)
item rocky9        Rocky Linux 9.4 (Kickstart)
item rescue        SystemRescue 11.x
item shell         Drop to iPXE shell
choose target || goto shell
goto ${target}

:ubuntu2404
kernel http://mirror.local/ubuntu/24.04/vmlinuz auto=true priority=critical preseed/url=http://mirror.local/preseed/ubuntu24.cfg netcfg/get_hostname=${hostname}
initrd http://mirror.local/ubuntu/24.04/initrd.gz
boot

:rocky9
kernel http://mirror.local/rocky/9.4/BaseOS/x86_64/os/images/pxeboot/vmlinuz inst.ks=http://mirror.local/kickstart/rocky9.ks ip=dhcp
initrd http://mirror.local/rocky/9.4/BaseOS/x86_64/os/images/pxeboot/initrd.img
boot

:rescue
chain http://mirror.local/rescue/sysresccd.ipxe

:shell
echo Type 'exit' to return to menu
shell

This menu demonstrates three key patterns: parameterized kernel arguments for unattended installs, variable substitution (${hostname}) pulled from DHCP or iPXE variables, and fallback safety via the shell option. The auto=true priority=critical flags tell Debian/Ubuntu installers to suppress all interactive prompts when combined with a valid preseed file.

A common mistake is hardcoding IPs in kernel lines. Always use hostnames resolvable by your PXE network's DNS (which dnsmasq can serve locally). This makes mirror migrations transparent to boot configs. For deeper integration with configuration management after install, pass cloud-init user-data URLs directly in kernel parameters rather than embedding configs in initrds.

Power OnDHCP DiscoverTFTP Get iPXEExecute MenuHTTP Kernel+InitrdUnattended Installcloud-init RunChainloadFetch ArtifactsBoot Installer
End-to-end PXE Boot for Automated OS Installs sequence: from firmware initialization through cloud-init execution.

What is the difference between preseed, kickstart, and cloud-init?

Choosing the right automation method depends on your distribution and long-term maintenance strategy. Each has distinct strengths, and mixing them incorrectly causes subtle failures.

MethodDistributionsBest ForLimitations
PreseedDebian, UbuntuFull offline installs, partitioning controlDeprecated in Ubuntu 24.04+, verbose syntax
KickstartRHEL, Rocky, AlmaEnterprise RHEL clones, %post scriptingRHEL-specific, limited cross-distro portability
AutoinstallUbuntu 20.04+Modern Ubuntu, YAML-based, SubiquityRequires curtin/cloud-init integration
cloud-initAll major distrosPost-install config, user setup, package installNot a full installer replacement, runs after base OS

In 2026, my recommendation is clear: use Autoinstall for Ubuntu and Kickstart for RHEL derivatives to handle disk partitioning and base package selection, then delegate all post-install configuration to cloud-init. This separation means your installer config rarely changes, while cloud-init user-data evolves with your application needs. It also aligns with immutable infrastructure principles covered in my golden images with Packer guide, where the base image stays static and runtime configuration is injected.

Never embed secrets in preseed or kickstart files served over HTTP. Use cloud-init's ability to fetch secrets from HashiCorp Vault or AWS Secrets Manager at runtime instead. If you must include credentials during install, serve those files over HTTPS with client certificate authentication—a pattern I detail in my SSH hardening guide for defense-in-depth.

How do you troubleshoot PXE boot failures systematically?

PXE failures fall into four categories, each with distinct diagnostic approaches. Work through them in order; jumping ahead wastes time.

  1. No DHCP response: Verify bind-interfaces is set, check firewall rules for UDP 67/68, and confirm the client's NIC is PXE-enabled in firmware. Use tcpdump -i eth1 port 67 or port 68 on the server to see if DISCOVER packets arrive. Missing responses usually mean wrong interface binding or VLAN misconfiguration.
  2. TFTP timeout: Confirm file permissions (chmod 644, owned by tftp:tftp), verify SELinux/AppArmor isn't blocking access, and test manually with tftp 10.10.50.1 -c get ipxe.efi from another host. TFTP has no error codes beyond "file not found," so permission issues manifest as timeouts.
  3. iPXE script errors: Enable debug output by adding set debug all at the top of your boot.ipxe. Common issues include missing #!ipxe shebang, incorrect variable expansion, or HTTP 404s on kernel paths. Always validate URLs with curl before testing in PXE.
  4. Installer hangs: Switch to verbose console output by removing quiet splash from kernel lines. For Ubuntu Autoinstall, add debug-cloudinit to kernel params. Check virtual consoles (Ctrl+Alt+F2-F6) for installer logs. Most hangs stem from missing preseed answers or network connectivity during package download.

Keep a known-good iPXE shell entry in your menu indefinitely. When automation fails, dropping to the shell lets you manually test DHCP (dhcp), ping mirrors (ping mirror.local), and fetch files (chain http://...) without rebooting. This alone cuts troubleshooting time by 80% in my experience.

PXE Boot FailureNo DHCP OfferTFTP TimeoutiPXE ErrorInstall HangCheck bind-interfacesVerify VLAN/Firewalltcpdump port 67File permissionsSELinux/AppArmorManual tftp testAdd set debug allValidate HTTP URLsCheck shebang lineRemove quiet splashCheck Alt+F2-F6Verify preseed/auto
Systematic troubleshooting decision tree for PXE Boot for Automated OS Installs failures across all protocol stages.

Implementing PXE Boot for Automated OS Installs in Production

PXE Boot for Automated OS Installs transforms bare-metal provisioning from a manual chore into a repeatable, auditable engineering process. Start with a dedicated provisioning VLAN isolated from production traffic, validate every boot path with both BIOS and UEFI clients, and integrate cloud-init early to avoid installer bloat. Monitor your dnsmasq logs and mirror bandwidth—PXE storms during fleet deployments can saturate links faster than expected. If you are designing new infrastructure or migrating legacy provisioning systems and need hands-on guidance, reach out to discuss your environment.

Frequently Asked Questions

PXE boot allows networked computers to load an operating system installer directly from a server without local media. It uses DHCP and TFTP protocols to deliver boot images, enabling mass deployment of standardized OS configurations across bare metal infrastructure efficiently.

You need a DHCP server for IP assignment and next-server options, a TFTP server hosting bootloader files, and an HTTP or NFS server storing installation repositories. Tools like dnsmasq, iPXE, and Foreman simplify managing these interconnected services for reliable automated provisioning workflows.

Yes, it adds scripting and HTTP support.

Absolutely. Preseed, Kickstart, Autoinstall, and answer files handle unattended Linux setups, while Windows uses autounattend.xml. Your PXE menu simply chains to the appropriate bootloader and passes the correct automation file path via kernel parameters or EFI variables.

Standard PXE lacks encryption, exposing configs to sniffing. Mitigate risks by isolating provisioning traffic on dedicated VLANs, using HTTPS for kickstarts where supported, signing bootloaders with Secure Boot keys, and restricting TFTP access through firewall rules to authorized subnets only.

Timeouts usually stem from misconfigured DHCP option 66/67 values, unreachable TFTP servers, or firewall blocks on UDP port 69. Verify network connectivity with tcpdump, confirm filename paths match exactly, and ensure SELinux or AppArmor permits TFTP daemon file access.

Yes. UEFI needs distinct bootloader binaries like shimx64.efi instead of pxelinux.0. Configure DHCP vendor-class matching to serve correct filenames per firmware type. Modern setups use iPXE universal binaries that detect architecture automatically, reducing dual-stack maintenance complexity significantly.

Embed ansible-pull commands in your kickstart or cloud-init scripts to run playbooks immediately after first boot. Alternatively, deploy a lightweight agent via PXE that registers with AWX or Semaphore for centralized orchestration, ensuring consistent configuration management from initial provision onward.

Initial lab setup takes four to eight hours.

PXE eliminates physical media handling, enables simultaneous multi-node installs, and centralizes image version control. USB remains useful for air-gapped environments but scales poorly beyond ten nodes. Network booting reduces human error and ensures identical baselines across entire fleets.

Most public clouds bypass PXE entirely, using metadata-driven cloud-init instead. However, private clouds like OpenStack, MAAS, or Harvester fully support PXE for bare metal and virtual machine provisioning. Use cloud-native tooling when available; reserve PXE for on-premises or hybrid infrastructure scenarios.

Plan for 500Mbps minimum per ten simultaneous nodes.

Maintain versioned directories on your HTTP/TFTP server and update DHCP or iPXE menu entries atomically. Test new images against staging hardware first. Use symbolic links pointing to current stable releases, allowing instant rollback by relinking if production deployments encounter unexpected failures during rollout.

Foreman provides GUI-driven template management, Smart Proxy distribution, integrated Puppet/Ansible orchestration, and audit logging. Manual setups work for small labs but become error-prone at scale. Foreman abstracts DHCP/TFTP complexity while enforcing consistency, making it ideal for teams managing hundreds of heterogeneous nodes.

Check dhcpd.log for lease offers, tftpd logs for file transfer errors, and web server access logs for kickstart retrieval failures. On the client side, enable verbose iPXE debugging or review anaconda/cloud-init logs. Correlate timestamps across all three layers to pinpoint exact failure points quickly.