< BACK
System administration mini tutorials and how-to guides
Tutorial: installing a PXE server for networking booting on Ubuntu 18.04.4 LTS
Updated: 2020-08-06
Overview
This describes a basic approach for the setup of running a PXE
server on an Ubuntu machine using dnsmasq
and serving images via NFS
within a network behind a router, in my case Fritzbox.
Sources and references
This guide was adapted from the following sources:
- https://forum-raspberrypi.de/forum/thread/13701-pxe-mit-fritzbox-via-raspberry/
- https://www.tecmint.com/install-ubuntu-via-pxe-server-using-local-dvd-sources/
- https://linuxhint.com/pxe_boot_ubuntu_server/
The following references may also be helpful:
- https://butwt.wordpress.com/2020/06/29/pxe-booting-ubuntu-20-04-installer/
- https://www.german-syslinux-blog.de/syslinux-6-04-ubtuntu-netzwerkinstallation-einrichten/
- https://askubuntu.com/questions/1254390/ubuntu-20-04-lts-does-not-mount-nfs-when-booting-pxe/1264245#1264245
- https://askubuntu.com/questions/1235723/automated-20-04-server-installation-using-pxe-and-live-server-image
- https://wiki.ubuntuusers.de/Downloads/Netzwerkinstallation/ (in German)
Setup
IP addresses
In my example, the IP addresses are allocated as follows:
Device | IP address/range |
---|---|
Network address | 192.168.178.0 |
Gateway (router) | 192.168.178.1 |
Server running PXE | 192.168.178.100 |
Required packages on Ubuntu
sudo apt install -y nfs-kernel-server
sudo apt install -y syslinux pxelinux dnsmasq
Installation process
This installation will use the following path for the tftp
server:
/srv/tftp
Configuration for dnsmasq
Set the variables as appropriate before running the following bash
commands to create the configuration:
NETWORK_ADDR="192.168.178.0"
GW_ADDR="192.168.178.1"
OWN_ADDR="192.168.178.100"
TFTP_ROOT=/srv/tftp
FILE="/etc/dnsmasq.conf"
[ -f $FILE ] && sudo cp --force --backup=numbered "$FILE" "$FILE" # backup any existing conf
read -r -d '' VAR <<EOF
log-dhcp
resolv-file=/etc/resolv.conf.dnsmasq
domain-needed
bogus-priv
dhcp-range=${NETWORK_ADDR},proxy
dhcp-boot=pxelinux.0,${OWN_ADDR},${NETWORK_ADDR}
pxe-prompt="Press F8 for menu.", 8
pxe-service=X86PC, "Legacy Network boot", /srv/tftp/pxelinux
pxe-service=X86-64_EFI,"EFI Network boot", /srv/tftp/pxelinux
# local tftp-server
enable-tftp
tftp-root=${TFTP_ROOT}
EOF
echo "$VAR" | sudo tee "$FILE"
The file should now look like this:
log-dhcp
resolv-file=/etc/resolv.conf.dnsmasq
domain-needed
bogus-priv
dhcp-range=192.168.178.0,proxy
dhcp-boot=pxelinux.0,192.168.178.100,192.168.178.0
pxe-prompt="Press F8 for menu.", 8
pxe-service=X86PC, "Legacy Network boot", /srv/tftp/pxelinux
pxe-service=X86-64_EFI,"EFI Network boot", /srv/tftp/pxelinux
# local tftp-server
enable-tftp
tftp-root=/srv/tftp
Now create the resolv
file
GW_ADDR="192.168.178.1"
FILE="/etc/resolv.conf.dnsmasq"
[ -f $FILE ] && sudo cp --force --backup=numbered "$FILE" "$FILE"
read -r -d '' VAR <<EOF
nameserver ${GW_ADDR} # gateway or router IP
# if necessary further nameservers (external)
nameserver 8.8.8.8
nameserver 8.8.8.4
EOF
echo "$VAR" | sudo tee "$FILE"
Then restart the dnsmasq
service:
sudo service dnsmasq restart
At this point booting from somewhere on the network (in this case using VirtualBox
) could look something like this:
and in the /var/log/syslog
file the following lines should show up:
Aug 2 11:32:50 elqt dnsmasq-dhcp[15024]: 3170770957 available DHCP subnet: 192.168.178.0/255.255.255.0
Aug 2 11:32:50 elqt dnsmasq-dhcp[15024]: 3170770957 vendor class: PXEClient:Arch:00000:UNDI:002001
Aug 2 11:32:50 elqt dnsmasq-dhcp[15024]: 3170770957 user class: iPXE
Aug 2 11:32:50 elqt dnsmasq-dhcp[15024]: 3170770957 PXE(enp2s0) 08:00:27:38:96:d0 proxy
Aug 2 11:32:50 elqt dnsmasq-dhcp[15024]: 3170770957 tags: enp2s0
Aug 2 11:32:50 elqt dnsmasq-dhcp[15024]: 3170770957 bootfile name: pxelinux.0
Aug 2 11:32:50 elqt dnsmasq-dhcp[15024]: 3170770957 next server: 192.168.178.0
Aug 2 11:32:50 elqt dnsmasq-dhcp[15024]: 3170770957 broadcast response
Aug 2 11:32:50 elqt dnsmasq-dhcp[15024]: 3170770957 sent size: 1 option: 53 message-type 2
Aug 2 11:32:50 elqt dnsmasq-dhcp[15024]: 3170770957 sent size: 4 option: 54 server-identifier 192.168.178.100
Aug 2 11:32:50 elqt dnsmasq-dhcp[15024]: 3170770957 sent size: 9 option: 60 vendor-class 50:58:45:43:6c:69:65:6e:74
Aug 2 11:32:50 elqt dnsmasq-dhcp[15024]: 3170770957 sent size: 17 option: 97 client-machine-id 00:74:c4:a9:4a:a5:d3:46:45:81:f8:1d:fe:f3...
Aug 2 11:32:50 elqt dnsmasq-dhcp[15024]: 3170770957 sent size: 58 option: 43 vendor-encap 06:01:03:08:07:80:00:01:c0:a8:b2:64:09:16...
Aug 2 11:32:50 elqt dnsmasq-dhcp[15024]: 3170770957 available DHCP subnet: 192.168.178.0/255.255.255.0
Aug 2 11:32:50 elqt dnsmasq-dhcp[15024]: 3170770957 vendor class: PXEClient:Arch:00000:UNDI:002001
Aug 2 11:32:50 elqt dnsmasq-dhcp[15024]: 3170770957 user class: iPXE
Aug 2 11:32:58 elqt dnsmasq-dhcp[15024]: 0 available DHCP subnet: 192.168.178.0/255.255.255.0
Aug 2 11:32:58 elqt dnsmasq-dhcp[15024]: 0 vendor class: PXEClient:Arch:00000:UNDI:002001
Aug 2 11:32:58 elqt dnsmasq-dhcp[15024]: 0 user class: iPXE
Aug 2 11:32:58 elqt dnsmasq-dhcp[15024]: 0 PXE(enp2s0) 192.168.178.38 08:00:27:38:96:d0 /srv/tftp/pxelinux.0
Aug 2 11:32:58 elqt dnsmasq-dhcp[15024]: 0 tags: enp2s0
Aug 2 11:32:58 elqt dnsmasq-dhcp[15024]: 0 bootfile name: /srv/tftp/pxelinux.0
Aug 2 11:32:58 elqt dnsmasq-dhcp[15024]: 0 next server: 192.168.178.100
Aug 2 11:32:58 elqt dnsmasq-dhcp[15024]: 0 sent size: 1 option: 53 message-type 5
Aug 2 11:32:58 elqt dnsmasq-dhcp[15024]: 0 sent size: 4 option: 54 server-identifier 192.168.178.100
Aug 2 11:32:58 elqt dnsmasq-dhcp[15024]: 0 sent size: 9 option: 60 vendor-class 50:58:45:43:6c:69:65:6e:74
Aug 2 11:32:58 elqt dnsmasq-dhcp[15024]: 0 sent size: 17 option: 97 client-machine-id 00:74:c4:a9:4a:a5:d3:46:45:81:f8:1d:fe:f3...
Aug 2 11:32:58 elqt dnsmasq-dhcp[15024]: 0 sent size: 28 option: 43 vendor-encap 47:04:80:00:00:00:0a:13:08:50:72:65:73:73...
Aug 2 11:32:58 elqt dnsmasq-tftp[15024]: sent /srv/tftp/pxelinux.0 to 192.168.178.38
Aug 2 11:32:58 elqt dnsmasq-tftp[15024]: sent /srv/tftp/ldlinux.c32 to 192.168.178.38
Aug 2 11:32:58 elqt dnsmasq-tftp[15024]: file /srv/tftp/pxelinux.cfg/74c4a94a-a5d3-4645-81f8-1dfef39eea6f not found
Aug 2 11:32:58 elqt dnsmasq-tftp[15024]: file /srv/tftp/pxelinux.cfg/01-08-00-27-38-96-d0 not found
Aug 2 11:32:58 elqt dnsmasq-tftp[15024]: file /srv/tftp/pxelinux.cfg/C0A8B226 not found
Aug 2 11:32:58 elqt dnsmasq-tftp[15024]: file /srv/tftp/pxelinux.cfg/C0A8B22 not found
Aug 2 11:32:58 elqt dnsmasq-tftp[15024]: file /srv/tftp/pxelinux.cfg/C0A8B2 not found
Aug 2 11:32:58 elqt dnsmasq-tftp[15024]: file /srv/tftp/pxelinux.cfg/C0A8B not found
Aug 2 11:32:58 elqt dnsmasq-tftp[15024]: file /srv/tftp/pxelinux.cfg/C0A8 not found
Aug 2 11:32:58 elqt dnsmasq-tftp[15024]: file /srv/tftp/pxelinux.cfg/C0A not found
Aug 2 11:32:58 elqt dnsmasq-tftp[15024]: file /srv/tftp/pxelinux.cfg/C0 not found
Aug 2 11:32:58 elqt dnsmasq-tftp[15024]: file /srv/tftp/pxelinux.cfg/C not found
Aug 2 11:32:58 elqt dnsmasq-tftp[15024]: sent /srv/tftp/pxelinux.cfg/default to 192.168.178.38
Aug 2 11:32:58 elqt dnsmasq-tftp[15024]: sent /srv/tftp/vesamenu.c32 to 192.168.178.38
Aug 2 11:32:58 elqt dnsmasq-tftp[15024]: sent /srv/tftp/libcom32.c32 to 192.168.178.38
Aug 2 11:32:58 elqt dnsmasq-tftp[15024]: sent /srv/tftp/libutil.c32 to 192.168.178.38
Aug 2 11:32:58 elqt dnsmasq-tftp[15024]: sent /srv/tftp/pxelinux.cfg/default to 192.168.178.38
Configuring the live netboot files and live image
Creating file structure from ISO image
Obtain the ISO image, e.g.
wget https://releases.ubuntu.com/18.04/ubuntu-18.04.4-desktop-amd64.iso
Then create a mount point and mount it, create necessary directories and copy files as follows, noting that for recent Ubuntu releases (e.g. 20.04) there is a hidden .disk
subdirectory which needs to be copied so use a .
(dot) with the cp
command:
sudo mkdir /mnt/ubu-1804/
sudo mount -o loop ubuntu-18.04.4-desktop-amd64.iso /mnt/ubu-1804/
sudo mkdir -p /srv/{nfs,tftp}/ubuntu1804
sudo cp -Rfv /mnt/ubu-1804/. /srv/nfs/ubuntu1804/
sudo cp -v /srv/nfs/ubuntu1804/casper/{vmlinuz,initrd*} /srv/tftp/ubuntu1804/
after which the ISO image can be unmounted and deleted or archived.
sudo umount /mnt/ubu-1804
This step can be automated using variables to facilitate further images being added later:
MNT_PT="/mnt/ubu-1804"
ISO="ubuntu-18.04.4-desktop-amd64.iso"
SUBDIR="ubuntu1804"
sudo mkdir ${MNT_PT}
sudo mount -o loop "${ISO}" "${MNT_PT}"
sudo mkdir -pv /srv/{nfs,tftp}/${SUBDIR}
sudo cp -iRfv ${MNT_PT}/. /srv/nfs/${SUBDIR} # copy contents of ISO to nfs dir
sudo cp -iv ${MNT_PT}/casper/{vmlinuz,initrd*} /srv/tftp/${SUBDIR}
sudo umount "${MNT_PT}" # unmount iso
Creating the PXE boot entry
Create the tftp
root directory and pxelinux.cfg
directory in a single step:
sudo mkdir -p /srv/tftp/pxelinux.cfg
Ubuntu 18.04 LTS Desktop uses casper
for booting into live mode and casper
only supports network boot via NFS. As such booting via PXE requires a network accessible NFS server. This will have been installed and setup via apt
above and a nfs
share directory /srv/nfs
also created above.
Add the following line to /etc/exports
/srv/nfs *(ro,sync,no_wdelay,insecure_locks,no_root_squash,insecure,no_subtree_check)
thus and finally make this nfs
share available:
if ! egrep -q '^/srv/nfs' /etc/exports; then \
echo "/srv/nfs *(ro,sync,no_wdelay,insecure_locks,no_root_squash,insecure,no_subtree_check)"\
| sudo tee -a /etc/exports; \
fi
sudo exportfs -a
Now copy the PXE files as follows
sudo cp -v /usr/lib/PXELINUX/pxelinux.0 /srv/tftp/ -i
sudo cp -v /usr/lib/syslinux/modules/bios/{ldlinux.c32,libcom32.c32,libutil.c32,vesamenu.c32} /srv/tftp -i
Create the PXE bootloader’s default configuration file:
OWN_ADDR="192.168.178.100"
LABEL="18040001" # label entry identifier (should be unique)
TITLE="Install Ubuntu 18.04 LTS Desktop"
DIRPATH="ubuntu1804" # relative to /srv/tftp and /srv/nfs
FILE="/srv/tftp/pxelinux.cfg/default"
[ -f $FILE ] && sudo cp --force --backup=numbered "$FILE" "$FILE"
# determine whether ``initrd`` or e.g. ``initrd.lz`` is required
for INITRD in /srv/tftp/${SUBDIR}/initrd*; do INITRD="${INITRD##*/}"; done
read -r -d '' VAR <<EOF
default vesamenu.c32
label ${LABEL}
\tmenu label ^${TITLE}
\tmenu default
\tkernel ${DIRPATH}/vmlinuz
\tappend initrd=${DIRPATH}/${INITRD} boot=casper netboot=nfs nfsroot=${OWN_ADDR}:/srv/nfs/${DIRPATH}/ locale=en_US keyboard-configuration/layoutcode=de console-setup/layoutcode=de nosplash toram ---
EOF
echo "$VAR" | sudo tee "$FILE"
which results in the following configuration:
default vesamenu.c32
label 18040001
menu label ^Install Ubuntu 18.04 LTS Desktop
menu default
kernel ubuntu1804/vmlinuz
append initrd=ubuntu1804/initrd boot=casper netboot=nfs nfsroot=192.168.178.100:/srv/nfs/ubuntu1804/ locale=en_US keyboard-configuration/layoutcode=de console-setup/layoutcode=de nosplash toram ---
Note that in my configuration, above, I deliberately chose to install in English (locale=en_US
) but my keyboard layout and configuration is German. These can be adapted or left out.
For Ubuntu 20.04
releases the append
line is somewhat different and requires:
ip=dhcp
The following works for me:
append initrd=ubu-20.04-mint-20-cinnamon/initrd.lz nfsroot=192.168.178.100:/srv/nfs/ubu-20.04-mint-20-cinnamon ro netboot=nfs file=/cdrom/preseed/linuxmint.seed boot=casper ip=dhcp -- debian-installer/language=en console-setup/layoutcode=de keyboard-configuration/layoutcode=de keyboard-configuration/variant=German
or using variables,
append initrd=${DIRPATH}/${INITRD} nfsroot=${OWN_ADDR}:/srv/nfs/${DIRPATH} ro netboot=nfs file=/cdrom/preseed/linuxmint.seed boot=casper ip=dhcp nosplash -- debian-installer/language=en console-setup/layoutcode=de keyboard-configuration/layoutcode=de keyboard-configuration/variant=German
It might be necessary at this point to restart dnsmasq
and re-read NFS
exports:
sudo service dnsmasq restart
sudo exportfs -a
Booting from somewhere on the network should look something like this:
Note: When creating additional network bootable images be sure to make the label entry identifier unique!
I hope this tutorial and guide to setting up a PXE boot server on Ubuntu
was helpful.