In this post I’m going to talk about booting raspios from your server – Network Booting, or PXE booting as it’s often called.
PXE booting (pronounced “pixie booting”) uses two common protocols – the Dynamic Host Configuration Protocol (DHCP) and the Trivial File Transfer Protocol (TFTP). As we’ll see below, setting these up on a Debian-based server is relatively straightforward. Roughly, the process is:
- download the raspios image you want to use;
- mount and copy it to an NFS share;
- create a TFTP mount point; and
- configure dnsmasq to point to it.
Setting up the client (a Raspberry Pi) for PXE booting is the subject of a different blog post. For now, the below will help you set up the server.
Note in all code below, a ‘\’ indicates a line continuation only and is done here for presentation purposes, ie. when typing out the code in a terminal you don’t actually type the ‘\’ but, instead, type the multple lines separated by the ‘\’ as a single command.
Install the tools you’ll need
We’re going to need unzip
to decompress the .zipped raspios image file we’ll download from the raspberrypi.org website. We’ll need kpartx
to help mount that (unzipped) .img file to some directories we’ll create – so that we can copy them across to our nfs share. nfs-kernel-server
is needed to serve the nfs share that will contain the operating system(s) that will be available to the client(s). dnsmasq
will be the magic that answers broadcast requests from our clients trying to pxeboot.
$ cd ~
$ sudo apt-get update
$ sudo apt-get upgrade
$ sudo apt-get install unzip kpartx \
nfs-kernel-server dnsmasq
Download and copy your OS to an NFS share
Download a raspios image and unzip it and mount the .img file.
$ wget https://downloads.raspberrypi.org \
/raspios_armhf/images/raspios_armhf-2020-08-24 \
/2020-08-20-raspios-buster-armhf.zip
$ unzip 2020-08-24-raspios-buster-armhf.zip
Now use kpartx
to create device nodes for each of the two partitions contained in the unzipped .img.
$ sudo kpartx -a 2020-08-24-raspios-buster-armhf.img
Create a new directory and mount the second partition (ie. the one containing all of the Raspberry Pi OS except the boot partition).
$ mkdir rootmnt
$ sudo mount /dev/mapper/loop0p2 rootmnt/
Create a directory for use as an NFS share and copy all of the OS files into it.
$ sudo mkdir -p /nfs/rpi
$ sudo cp -a rootmnt/* /nfs/rpi/
Now repeat this with the first partition (ie. the boot partition).
$ mkdir bootmnt
$ sudo mount /dev/mapper/loop0p1 bootmnt/
$ sudo mkdir /nfs/rpi/boot
$ sudo cp -a bootmnt/* /nfs/rpi/boot
Now unmount the .img as you no longer need it.
$ sudo umount rootmnt
$ sudo umount bootmnt
$ sudo kpartx -d 2020-08-24-raspios-buster-armhf.img
Now we need to do a few file manipulations.
First, we don’t need or want the booted OS to mount any disks so we remove the default mount points in the OS:
$ sudo sed -i /UUID/d /nfs/rpi/etc/fstab
Next, we create the ssh file to ensure OpenSSH is available once booted This is optional. If you don’t foresee the need to ssh into your client, then you can skip this step. If you are going headless, however, you’ll want to do it:
$ sudo touch /nfs/rpi/boot/ssh
Then, set the kernel parameters to be passed in at boot so that the kernel mounts the remote NFS share as the file system:
$ echo "console=serial0,115200 console=tty root=/dev/nfs \
nfsroot=192.168.1.{my ip}:/nfs/rpi,vers=3 rw rootwait \
ip=dhcp elevator=deadline" \
| sudo tee nfs/rpi/boot/cmdline.txt
Finally, finish setting up the nfs share:
$ echo "/nfs/rpi *(rw,sync,no_subtree_check,no_root_squash)"\
| sudo tee -a /etc/exports
$ sudo systemctl enable rpcbind
$ sudo systemctl enable nfs-kernel-server
$ sudo systemctl restart rpcbind
$ sudo systemctl restart nfs-kernel-server
Create the TFTP mount point
Now that our OS is setup on the nfs share, we need to create and edit the network boot infrastructure. Note that, below, I’m assuming the serial number of the client is deadbeef
. Make sure you subsitute your client machine’s serial number where you see any reference to deadbeef
:
$ sudo mkdir -p /tftp/deadbeef
$ echo "/nfs/rpi/boot /tftp/deadbeef none defaults,bind \
0 0" | sudo tee -a /etc/fstab
$ sudo mount /tftp/deadbeef
$ sudo chmod 777 /tftp
Now that we’ve set up the tftp
directory and mounted our /nfs/rpi
install to it, we need to configure dnsmasq
to serve it up when requested by the client (at boot).
Configure dnsmasq
Set up dnsmasq:
$ echo "dhcp-range=192.168.1.255,proxy" | sudo tee \
-a /etc/dnsmasq.conf
$ echo "log-dhcp" | sudo tee -a /etc/dnsmasq.conf
$ echo "enable-tftp" | sudo tee -a /etc/dnsmasq.conf
$ echo "tftp-root=/tftp" | sudo tee \
-a /etc/dnsmasq.conf
$ echo "pxe-service=0,'Raspberry Pi Boot'" | sudo tee \
-a /etc/dnsmasq.conf
$ sudo systemctl enable dnsmasq
$ sudo systemctl restart dnsmasq
To test it all works, we start watching the server’s logs:
$ sudo tail -f /var/log/daemon.log
and now start the client. Any errors that cause a failure of the client to boot, should show up here. If you’ve followed the above, there shouldn’t be any.
Leave a Reply