Introduction
PXE (pronounced "pixie") enables:
- Network boot (PXE-capable NIC)
- Automated OS install (Debian, Ubuntu, ESXi)
- Mass deployment without manual intervention
- Fast re-install on failure
- Boot in rescue / live mode
Use cases: datacenter with 50 identical servers to deploy, provisioning rack, Proxmox lab.
How it works
PXE Client Switch PXE Server
| 1. DHCP DISCOVER (broadcast) | |
|------------------------------->| |
| | 2. DHCPOFFER + filename + |
| | next-server |
|<-------------------------------|----------------------------|
| 3. TFTP GET pxelinux.0 | |
|---------------------------------|--------------------------->|
| 4. Bootloader (pxelinux/grub) | |
| loads kernel + initrd | |
| 5. Kernel boots, mounts NFS | |
| or HTTP for preseed | |
Prerequisites
- Linux VPS Debian 12 / Ubuntu 24.04 (the PXE server)
- 2 IPs on same LAN as clients
- Root access
- No existing DHCP server, OR ability to configure existing DHCP
Step 1: Install dnsmasq + tftpd
sudo apt update
sudo apt install -y dnsmasq tftpd-hpa pxelinux syslinux-common nginx
Step 2: Configure dnsmasq (DHCP + DNS + TFTP)
/etc/dnsmasq.d/pxe.conf:
interface=eth0
bind-interfaces
dhcp-range=192.168.1.100,192.168.1.200,12h
dhcp-option=3,192.168.1.1
dhcp-option=6,1.1.1.1,8.8.8.8
enable-tftp
tftp-root=/srv/tftp
dhcp-boot=pxelinux.0
dhcp-match=set:bios,option:client-arch,0
dhcp-match=set:efi64,option:client-arch,7
dhcp-boot=tag:bios,pxelinux.0
dhcp-boot=tag:efi64,grubx64.efi
log-dhcp
sudo systemctl restart dnsmasq
Step 3: Download pxelinux and bootloaders
sudo mkdir -p /srv/tftp/pxelinux.cfg
sudo cp /usr/lib/PXELINUX/pxelinux.0 /srv/tftp/
sudo cp /usr/lib/syslinux/modules/bios/ldlinux.c32 /srv/tftp/
sudo cp /usr/lib/syslinux/modules/bios/menu.c32 /srv/tftp/
sudo cp /usr/lib/syslinux/modules/bios/libutil.c32 /srv/tftp/
sudo cp /usr/lib/syslinux/modules/bios/vesamenu.c32 /srv/tftp/
sudo cp /usr/lib/syslinux/modules/bios/libcom32.c32 /srv/tftp/
For UEFI:
sudo apt install -y grub-efi-amd64-signed shim-signed
sudo cp /usr/lib/grub/x86_64-efi-signed/grubx64.efi /srv/tftp/
Step 4: PXE menu
/srv/tftp/pxelinux.cfg/default:
DEFAULT vesamenu.c32
PROMPT 0
MENU TITLE VeryCloud PXE Boot Menu
TIMEOUT 100
LABEL local
MENU LABEL Boot from local disk
LOCALBOOT 0
LABEL debian12
MENU LABEL Install Debian 12 (auto)
KERNEL debian-installer/amd64/linux
APPEND vga=788 initrd=debian-installer/amd64/initrd.gz auto=true priority=critical preseed/url=http://192.168.1.5/preseed.cfg ---
LABEL ubuntu24
MENU LABEL Install Ubuntu 24.04 (auto)
KERNEL ubuntu/casper/vmlinuz
APPEND initrd=ubuntu/casper/initrd autoinstall ds=nocloud-net\;s=http://192.168.1.5/autoinstall/ ---
LABEL rescue
MENU LABEL Rescue / live system
KERNEL debian-installer/amd64/linux
APPEND initrd=debian-installer/amd64/initrd.gz rescue/enable=true
Step 5: Download netboot images
Debian 12
sudo mkdir -p /srv/tftp/debian-installer/amd64
cd /srv/tftp/debian-installer/amd64
sudo wget http://ftp.debian.org/debian/dists/bookworm/main/installer-amd64/current/images/netboot/debian-installer/amd64/linux
sudo wget http://ftp.debian.org/debian/dists/bookworm/main/installer-amd64/current/images/netboot/debian-installer/amd64/initrd.gz
Ubuntu 24.04
sudo mkdir -p /srv/tftp/ubuntu/casper
cd /tmp
sudo wget https://releases.ubuntu.com/24.04/ubuntu-24.04-live-server-amd64.iso
sudo mount -o loop ubuntu-24.04-live-server-amd64.iso /mnt
sudo cp /mnt/casper/vmlinuz /srv/tftp/ubuntu/casper/
sudo cp /mnt/casper/initrd /srv/tftp/ubuntu/casper/
sudo umount /mnt
Step 6: HTTP server for preseed
/etc/nginx/sites-available/pxe:
server {
listen 80;
server_name 192.168.1.5;
root /srv/www;
location / {
autoindex on;
}
}
sudo mkdir -p /srv/www
sudo ln -s /etc/nginx/sites-available/pxe /etc/nginx/sites-enabled/
sudo systemctl restart nginx
Step 7: Debian preseed
/srv/www/preseed.cfg:
d-i debian-installer/locale string en_US.UTF-8
d-i keyboard-configuration/xkb-keymap select us
d-i netcfg/choose_interface select auto
d-i netcfg/get_hostname string server
d-i mirror/country string manual
d-i mirror/http/hostname string deb.debian.org
d-i mirror/http/directory string /debian
d-i passwd/root-login boolean true
d-i passwd/root-password-crypted password $6$rounds=4096$xxxx...
d-i passwd/user-fullname string Admin
d-i passwd/username string admin
d-i passwd/user-password-crypted password $6$rounds=4096$yyyy...
d-i clock-setup/utc boolean true
d-i time/zone string UTC
d-i clock-setup/ntp boolean true
d-i partman-auto/method string regular
d-i partman-auto/choose_recipe select atomic
d-i partman-partitioning/confirm_write_new_label boolean true
d-i partman/choose_partition select finish
d-i partman/confirm boolean true
d-i partman/confirm_nooverwrite boolean true
tasksel tasksel/first multiselect ssh-server
d-i pkgsel/include string openssh-server sudo curl
d-i grub-installer/bootdev string default
d-i grub-installer/only_debian boolean true
d-i finish-install/reboot_in_progress note
d-i preseed/late_command string \
in-target wget -O /root/post.sh http://192.168.1.5/post.sh; \
in-target chmod +x /root/post.sh; \
in-target /root/post.sh
Generate password hashes:
mkpasswd -m sha-512
Step 8: Post-install script
/srv/www/post.sh:
#!/bin/bash
mkdir -p /root/.ssh
echo "ssh-ed25519 AAAA... [email protected]" > /root/.ssh/authorized_keys
chmod 700 /root/.ssh
chmod 600 /root/.ssh/authorized_keys
apt-get update
apt-get install -y htop curl vim git fail2ban
sed -i 's/^#PasswordAuthentication yes/PasswordAuthentication no/' /etc/ssh/sshd_config
systemctl restart ssh
Step 9: Ubuntu autoinstall (cloud-init)
sudo mkdir -p /srv/www/autoinstall
sudo touch /srv/www/autoinstall/meta-data
/srv/www/autoinstall/user-data:
#cloud-config
autoinstall:
version: 1
locale: en_US.UTF-8
keyboard:
layout: us
identity:
hostname: server
username: admin
password: "$6$rounds=4096$xxxx..."
ssh:
install-server: true
authorized-keys:
- "ssh-ed25519 AAAA... [email protected]"
storage:
layout:
name: lvm
packages:
- htop
- curl
- vim
- git
late-commands:
- curtin in-target -- systemctl enable ssh
Step 10: Boot a machine in PXE
On client:
- Enter BIOS / UEFI
- Enable "Network Boot" / "PXE Boot"
- Set LAN first in boot order
- Save and restart
Machine should:
- DHCP DISCOVER
- Receive DHCPOFFER with next-server (your PXE)
- TFTP download pxelinux.0
- Show PXE menu
- Launch auto-install
Step 11: Logs and debug
sudo tail -f /var/log/syslog | grep dnsmasq
sudo journalctl -u dnsmasq -f
sudo journalctl -u tftpd-hpa -f
TFTP test:
tftp 192.168.1.5
> get pxelinux.0
> quit
Step 12: Foreman / Cobbler (going further)
For complex deployments:
- Cobbler: separate tutorial
- Foreman: full provisioning stack + reporting
- MAAS (Canonical): "Metal as a Service" for bare metal deployment
Troubleshooting
Client doesn't get DHCP
dnsmasqon correct interface- No firewall (port 67/UDP, 69/UDP)
- Client on right VLAN
- No competing DHCP
sudo tcpdump -i eth0 -n port 67 or port 68
TFTP "File not found"
sudo ls -la /srv/tftp/
sudo chmod -R 755 /srv/tftp/
sudo chown -R tftp:tftp /srv/tftp/
Very slow boot
TFTP is slow (UDP, small packets). Use iPXE to chainload via HTTP. See https://ipxe.org
Preseed fails silently
Press Esc then e at menu boot to edit kernel line. Add debconf/priority=low to see errors.
Useful commands
sudo systemctl status dnsmasq
sudo systemctl status tftpd-hpa
sudo systemctl status nginx
sudo journalctl -u dnsmasq -f
tftp 192.168.1.5 -c get pxelinux.0
ls -la /srv/tftp/
cat /srv/tftp/pxelinux.cfg/default
sudo apt install -y ipxe
Conclusion
PXE Boot enables fast and automated deployment:
- No physical media needed
- Reproducible (preseed / autoinstall)
- Essential in datacenters
Limits:
- Requires LAN control (DHCP)
- TFTP is slow (use iPXE + HTTP)
- Technical initial setup
Going further:
- iPXE for HTTP boot (10x faster)
- Cobbler to orchestrate multiple distros
- Foreman / MAAS for full provisioning
Resources
- PXELINUX docs: https://wiki.syslinux.org/wiki/index.php?title=PXELINUX
- Debian preseed: https://www.debian.org/releases/stable/amd64/apb.html
- Ubuntu autoinstall: https://ubuntu.com/server/docs/install/autoinstall
- iPXE: https://ipxe.org


















