Setup Network boot for installing OS using PXE server

article-featured-image

Whenever you are installing a new operating system in the machine, you insert your bootable USB drive in the system and often notice Network boot option in bootable devices. In this article, I'll be talking about how to install an operating system using that Network boot option. The best part is you don't even need a bootable USB drive to achieve this.

However, this process is only useful if you have multiple of systems running on the same network and you need to install a new OS in all of the machines. In his scenario, booting OS image from the Network boot option can be very effective. It'll save you lots of time and some amount of money.

To achieve this, we'll use iPXE which is an open-source Network boot firmware based on PXE (Preboot Execution Environment) which is a set of guidelines that are used to configure boot server and allow other systems on the same network to install OS using Network boot.

Prerequisites

I'll be using Linux Mint 21.2 as my PXE boot server to serve Ubuntu 22.04.3 LTS operating system image via Network boot. Linux 21.2 is based on Ubuntu 22.04, so there will be no issue if you decide to use Ubuntu 22.04 or even Ubuntu 20.04.6 as your PXE boot server because I have tested configuring the server on both of them without any issue. To get started, we need to install some packages and download Ubuntu 22.04.3 LTS ISO image.

  • Download ISO image of Ubuntu 22.04.3 LTS from here.
  • Install the required packages using the below command:
    $
    sudo apt update

    $
    sudo apt -y install build-essential liblzma-dev isolinux git nfs-kernel-server

Now that you have installed all the required packages, proceed to the next section and follow the rest of the process as described.

Network configuration of server

In this section, we will look into the concept of booting from a network, which solely stands on computer networkng. That is why, the server must be configured correctly. Follow the process carefully for the network configuration of server.

Assign static IP address

We need to assign a static IP address to the server in order to improve reliability and prevent unexpected connectivity issues for the client systems. Another possible reason is that dynamically allocated IP address changes over time which will lead to connectivity issues.

In order to modify the network connection, first you need to find the name of active network connection. Use the following command in the terminal:

$
nmcli --fields NAME con show --active|tail -n 1

This command will output the name of active network connection. You will get the result somewhat similar to this image:

get-connection-name

Now that we have the connection name, we can modify the connection to assign static IP address and DNS nameservers. Use the following command to do this:

$
sudo nmcli con mod "Wired connection 1" ipv4.method manual ipv4.address 192.168.1.221/24 ipv4.gateway 192.168.1.2 ipv4.dns 1.1.1.1,8.8.8.8
  • In this command, "Wired connection 1" is my connection name (you must replace it with your connection name that you find out using the previous command).
  • 192.168.1.221 is the static IP address that I'm assigning, replace it with the IP address available on your network.
  • /24 represents 255.255.255.0 subnet in CIDR notation, you should also change it according to your network.
  • 192.168.1.2 is my internet gateway address, replace it with the internet gateway address of your network.
  • 1.1.1.1,8.8.8.8 are the DNS servers that I'll be using. Assigning DNS servers manually is necessary. You can use also use these DNS servers or you can use custom servers if you want.

After setting the static IP, use sudo nmcli con reload and sudo systemctl restart NetworkManager commands to restart the network service. Now use the below command to check IP address:

$
ip add
You will get the result as shown in the below image: get-ip-details

Here you can see that my IP address has been changed. If your system still reflects the old IP address, you may need to use sudo reboot command to reboot your system.

After verifying IP change, test that your configuration is correct by pinging a public domain. Use the below command:

$
ping -c 3 google.com
This command should give you result something like this: get-ip-details

If you got the same output from ping command as the image above, then your static IP configuration is correct. You can also use sudo nmtui command for configuring static IP if you prefer GUI mode.

Configuring iPXE

To configure the iPXE boot server, we first need to create a directory and sub-directories that will contain iPXE configuration files, bootable binaries, and operating system files. Run this command:

$
sudo mkdir -p /pxe_netboot/{config,os-images,firmware}

Placing operating system files

I'm assuming that you have downloaded Ubuntu 22.04.3 LTS ISO image file as described in the prerequisites section. In order to access the ISO image files, first you need to mount it to a directory. Use this command:

$
sudo mount -o ~/Downloads/ubuntu-22.04.3-desktop-amd64.iso /mnt/ubuntu

This command will mount the ISO image file to /mnt/ubuntu directory. I'm assuming that your ISO file is in Downloads directory. If it's in another directory, update the path of file in the command.

Now create a sub-directory in /pxe_netboot/os-images/ directory using sudo mkdir /pxe_netboot/os-images/ubuntu-22.04.3 command. To copy operating system files into the newly created ubuntu directory, use the below command:

$
sudo rsync -avz /mnt/ubuntu/ /pxe_netboot/os-images/ubuntu-22.04.3/

If your system is missing rsync package, then use sudo apt install rsync to download it from the apt repository. This command will copy all the Ubuntu 22.04.3 operating system files in /pxe_netboot/os-images/ubuntu-22.04.3 directory while preserving all the file permissions and ownership along with directory structure.

Now that the ISO image file has served Its purpose, you can unmount it using:

$
sudo umount /mnt/ubuntu

Creating iPXE configuration file

After placing operating system files in the appropriate directory, We need to configure iPXE server where we will define how operating system files should be served over the network. Create a new iPXE configuration file in config sub-directory using the below command:

$
sudo nano /pxe_netboot/config/boot.ipxe
A new blank file will open. Copy and paste the following iPXE script in this file:
#!ipxe

set server_ip 192.168.1.221
set root_path /pxe_netboot
menu Select an OS to boot       # Just a label or description for boot menu
item ubuntu-22.04.3         Install Ubuntu Desktop 22.04.3 LTS
choose --default exit --timeout 10000 option && goto ${option}

:ubuntu-22.04.3
set os_root os-images/ubuntu-22.04.3
kernel tftp://${server_ip}/${os_root}/casper/vmlinuz
initrd tftp://${server_ip}/${os_root}/casper/initrd
imgargs vmlinuz initrd=initrd boot=casper maybe-ubiquity netboot=nfs ip=dhcp nfsroot=${server_ip}:${root_path}/${os_root} quiet splash ---
boot

This is a basic iPXE script file to automate the boot process. For better understanding, try to use the files and directory names as described in the article. Let's break it down and see what you might need to change according to your system:

  • In set server_ip 192.168.1.221, replace 192.168.1.221 with the IP address of your server.
  • set root_path /pxe_netboot is the main directory that will hold all the PXE boot server files including operating system files.
  • item ubuntu-22.04.3 Install Ubuntu Desktop 22.04 LTS is used to create a selectable entry in the boot menu, where ubuntu-22.04.3 is the identifier and Ubuntu Desktop 22.04.3 LTS is the label assigned to that identifier. This identifier will be used in the next step.
  • :ubuntu-22.04.3 is the target label that specifies a set of actions that should be executed when this target is selected from the boot menu. The target is mapped with an identifier that we defined in the previous line. Target label matching with the identifier will be executed.
  • set os_root os-images/ubuntu-22.04.3 used to specify the os_root variable and It's value os-images/ubuntu-22.04.3 which is the path of operating system directory that is relative to root_path variable.

Press Ctrl + x followed by y to save and close the file. Check iPXE Docs for refrence.

Building iPXE bootloader binaries

In this section we will configure the network boot configuration file and build iPXE bootloader binaries. We will build different binaries for various type of network boot scenarios. Use the below commands:

$
cd ~/Downloads && git clone https://github.com/ipxe/ipxe.git

$
cd ipxe/src && nano bootconfig.ipxe

First command will grab the required files for building bootloader binaries from iPXE GitHub repository. The second command will create and open a new iPXE script file. Add the following content in that file:

#!ipxe

dhcp
chain tftp://192.168.1.221/config/boot.ipxe

This is a very simple iPXE script that will specify the usage of DHCP for assigning IP addresses to network boot clients. It also uses TFTP (Trivial File Transport Protocol) to fetch and execute boot.ipxe that we created in the previous section.

Now that we have created all the required, It's time to build bootloader binaries. First make sure your current path is ~/Downloads/ipxe/src and use the following command:

$
make bin/ipxe.pxe bin/undionly.kpxe bin/undionly.kkpxe bin/undionly.kkkpxe bin-x86_64-efi/ipxe.efi EMBED=bootconfig.ipxe

This command will build various iPXE bootloader files for different types of network booting scenarios. It might take some time to build binaries. Lets have some overview of what each binary file can be used for:

  • ipxe.pxe is a PXE bootable image for netbooting using iPXE.
  • undionly.kpxe, undionly.kkpxe, and undionly.kkkpxe are another PXE bootable images for iPXE. These images are for older systems and they have evolved over time. Each image has more added features than the previous one.
  • ipxe.efi is an EFI bootable image and It allows network boot for 64-bit systems.
  • bootconfig.ipxe is the iPXE script file created in the previous step. Here, this file is being embedded in the build to customize the behavior of iPXE client during the boot process.

Now copy all the bootable binaries that we have build to /pxe_netboot/binary directory. Use the below command:

$
sudo cp bin/{ipxe.pxe,undionly.kpxe,undionly.kkpxe,undionly.kkkpxe} bin-x86_64-efi/ipxe.efi /pxe_netboot/firmware/

Now that we have created all the required network boot configuration files, build the bootable binaries, and placed the operating system files in the relevant directory. Your /pxe_netboot directory structure must look like this:

directory-structure

If your main PXE directory contains all the files and directories shown in the image above, then your iPXE configuration part is done.

Configuring dnsmasq on iPXE server

In the previous section, we talked about DHCP where the iPXE client will be assigned an IP address for booting from the network. This is where dnsmasq comes to play. Aside from It's feature of DNS server, it can also act as a lightweight DHCP server. dnsmasq is crucial for the process because iPXE clients need IP addresses to perform any kind of communication such as locating boot server.

dnsmasq can also act as tftp server because iPXE is usually dependent on it for fetching It's scripts and configuration files. To install dnsmasq, first we need to stop and disable systemd-resolved.service. Because both dnsmasq and systemd-resolved.service utilize port 53 and two services cannot use the same port at a time.

Use the following commands where first command will stop and disable the systemd-resolved.service, the second command will unlink Its configuration file, and third command will create a new file resolv.conf that will be used to specify DNS server that system should use for domain name resolution.

$
sudo systemctl stop --now systemd-resolved.service

$
sudo unlink /etc/resolv.conf

$
sudo nano /etc/resolv.conf
A new file is opened. Copy and paste the following content in this file:
nameserver 127.0.0.1
nameserver 8.8.8.8
nameserver 8.8.4.4

Here I'm using 8.8.8.8 & 8.8.4.4 as my primary & secondary nameserver by Google but you can replace them other DNS nameservers if you want.

Now install the dnsmasq package using this command:
$
sudo apt install dnsmasq

If you had stopped the systemd-resolved.service as described above, dnsmasq will be installed and start listening on port 53 successfully. Otherwise, you will get port is use error which means systemd-resolved.service is still active. Make sure to disable it first.

Now create a new dnsmasq configuration file but first keep the backup of original configuration file. Use the following command:

$
sudo mv /etc/dnsmasq.conf /etc/dnsmasq.conf-backup && sudo nano /etc/dnsmasq.conf

A new blank file will be opened. Copy and paste the following lines in it:

# Debug logging
log-debug

# server NIC name
interface=ens33
bind-interfaces

# This range(s) is for the public interface, where dnsmasq functions
# as a proxy DHCP server providing boot information but no IP leases.
# Any ip in the subnet will do, so you may just put your server NIC ip here.
dhcp-range=ens33,192.168.1.180,192.168.1.200,255.255.255.0,8h
dhcp-option=option:router,192.168.1.2   # Router IP address
dhcp-option=option:dns-server,8.8.8.8   # primary DNS server
dhcp-option=option:dns-server,8.8.4.4   # Secondary DNS server

# To use dnsmasq internal TFTP server
enable-tftp
tftp-root=/pxe_netboot

# BIOS configuration
dhcp-match=set:bios-x86,option:client-arch,0
dhcp-boot=tag:bios-x86,firmware/ipxe.pxe

# UEFI configuration
dhcp-match=set:efi-x86_64,option:client-arch,7
dhcp-match=set:efi-x86_64,option:client-arch,9
dhcp-boot=tag:efi-x86_64,firmware/ipxe.efi

Lets break it down, understand what each line means, and what you might need to change according to your system.

  • In interface=ens33, ens33 is my NIC (Network Interface Card) name. You must replace it with your interface card name. Use ls /sys/class/net | head -1 command in terminal to find the name of your active NIC.
  • Also specify your NIC name in dhcp-range= attribute. This attribute defines the range of IP addresses that are available for allocating to iPXE clients. You must select the same subnet and IP range as your iPXE server.
  • Option dhcp-option=option:router,192.168.1.1 specify the router IP address. Most of the routers are configured to work on 192.168.1.1 by default but you must check your router IP and replace it here if different. Use ip route | grep default command to know your router IP address. (I'm assuming your router IP is same as your internet gateway, which in most cases is true if not changed manually.)

Press Ctrl + x and then y to save and close the file. Your dnsmasq configuration is finished here. Use below command to restart dnsmasq service

$
sudo systemctl restart dnsmasq

Configuring NFS on iPXE server

In this section, we'll configure NFS (Network File System) on iPXE server for serving operating system files to iPXE clients over network boot. The package used for this process is nfs-kernel-server which we have already installed in prerequisites section. Use the below command to define export settings for /pxe_netboot directory:

$
echo "/pxe_netboot        *(ro,sync,no_wdelay,insecure_locks,no_root_squash,insecure,no_subtree_check)" | sudo tee -a /etc/exports

This command will append a new line in NFS exports file that will allow /pxe_netboot directory to be served on network boot with appropriate access rights.

We also need to assign a port to mountd service. mountd is an essential component of NFS that is responsible for NFS clients to mount NFS shared directories. If you are not utilizing any firewall (UFW or Firewalld) then you can skip this part. This method is for the UFW firewall to allow mountd. Firewalld may not need this.

Use this command to open NFS configuration file:
$
sudo nano /etc/nfs.conf

In this configuration file, go to mountd block and add port=13025 in new line. Now press Ctrl + x and then y to save and close the file. Your NFS configuration file should look like this:

port-added-in-mountd-block

NFS configuration is completed with this part. Use below command to restart NFS service:

$
sudo systemctl restart nfs-server.service

Managing UFW firewall for iPXE network boot

If you are not using any firewall, then you can probably skip this section though It's not a good idea to use network boot without a firewall. We are using multiple services in this process and all these services use different ports on system that need access through the firewall. Use the following commands to all firewall files in UFS:

$
sudo ufw allow bootps

$
sudo ufw allow 69/udp

$
sudo ufw allow 111

$
sudo ufw allow nsf

$
sudo ufw allow 13025

$
sudo ufw reload
Let's see what each allow rule does:
  • sudo ufw allow bootps allows bootps (Bootstrap protocol service) service that assigns new IP address to network devices on boot process.
  • sudo ufw allow 69/udp allows UDP port 69 which is used by TFTP to transfer boot configuration and bootloader binaries to iPXE clients.
  • sudo ufw allow 111 allows port 111 which is used by rpcbind service. This service must be allowed in firewall to establish a connection over NFS.
  • sudo ufw allow nfs allows NFS service through firewall. You can achieve the same by allowing port 2049 using sudo ufw allow 2049 command.
  • sudo ufw allow 13025 allows port 13025 through the firewall. This is the port we assign to mountd in the previous part. It is responsible for NFS clients mounting the NFS shared directory.
  • sudo ufw reload reloads the firewall to make the changes effective.

Now that all the services and components of iPXE network boot server are configured, It's time to test the server from client system.

Testing

Now go the any other system that is connected to the same network as your iPXE server is and boot your computer in network boot mode. Usually you can access boot menu by pressing F12 or F8 button while booting the system. You will see options something like this:

boot-menu

Based on your system, you might have different layouts and options in the boot menu, but the main thing is to select Network boot option from menu. Here, my network boot is under EFI Network option. Selecting it initializes the iPXE server as shown in below image:

ipxe-server-initializing

In iPXE server initialization, IP address is assigned to the client system and bootloader binaries are loaded. Then you will be presented with the OS menu where you can select the available operating systems to boot from. Check the image below:

ipxe-netboot-menu

As in our case, we only have Ubuntu in OS menu. Selecting this operating system will start the booting process for Ubuntu and installation process of Ubuntu 22.04.3 LTS will be started. This way you can install OS on client systems using network boot mode.

Suggested Posts:
LINUX post image
Understand SELinux module and manage security policies in Linux

This article is all about SELinux. It's a security component that protects Linux systems from …

SCRIPTS post image
Python script convert S3 bucket images to a PDF file

AWS S3 is an object-level storage service that can be utilized in various ways, …

LINUX post image
How to setup on-premise MySQL master and slave servers

In this article I'll be demonstrating how you can configure MySQL master and slave …

CLOUD post image
Setup AWS cross-account RDS MySQL master and slave servers

This article is about AWS RDS master and slave replication. I'll explain how to …

LINUX post image
Configure FastAPI with Nginx and uvicorn

This article is all about the configuration of FastAPI with uvicorn and then reverse …

Sign up or Login to post comment.

Comments (1)

6 months ago

Under "Placing operating system files" we are missing the option after -o which in this case for an iso file would be loop. So the whole command should be "sudo mount -o loop ~/Downloads/ubuntu-22.04.3-desktop-amd64.iso /mnt/ubuntu"

Sign up or Login to reply a comment