Skip to content

Preparing a Netboot Server

This guide covers explanation and implementation of the components required for a complete Pi Netboot capable server

In order to successfully Netboot a Pi, you need a server on your network capable of serving the necessary files over the correct protocols.

There are two protocols we will be using, TFTP and NFS. For simplicity we will be hosting these together on a single machine, and calling this our ‘Netboot’ server.

  • DHCP Server
    • DHCP (Dynamic Host Configuration Protocol) is responsible for dynamically assigning IP addresses and network configuration information to devices on a network.
    • If you’re reading this you likely already know what/where your DHCP server is. If you don’t, and you’re a home user, then it’s 99% built into your router.
    • We will be modifying our DHCP server settings to tell devices on the network where to look for the TFTP server.
  • TFTP Server
    • TFTP (Trivial File Transfer Protocol) is responsible for simple and lightweight file transfers between devices on a network, commonly used for tasks such as updating firmware or booting diskless devices.
    • It’s highly unlikely you already have a TFTP server running, we will be creating a new one specifically for bootstrapping pi devices
    • The TFTP server will provide the Pi with it’s boot partiton files
    • We’ll be running this in a container image served via Docker 🐋
  • NFS Server
    • NFS (Network File System) is responsible for enabling remote access and sharing of files between networked systems in a transparent and platform-independent manner.
    • Again, very unlikely you already have an NFS server. But we’ll be standing up a new bootstrap specific one anyway
    • The NFS server will provide the Pi with it’s OS partition files
    • Again, container image served via Docker 🐳
  • Netboot client
    • The Raspberry Pi itself🥧

Typically, a server (of any kind) would run on an always-on machine within your network. However since we are starting from zero here and assuming there currently is no server, we will be running our initial bootstrap server via Docker on our Linux machine/VM.

docker-compose.yml
services:
# TFTP server for hosting boot files
pi-tftp:
image: andrewkidd/tftp-server
container_name: pi-tftp
network_mode: host
profiles:
- netboot
- debian-netboot
volumes:
- ./README.md:/var/tftpboot/test.txt:ro
- netboot-boot-data:/var/tftpboot
ports:
- 69:69/udp
healthcheck:
test: ["CMD", "test", "-f", "/var/tftpboot/test.txt"]
interval: 1m
timeout: 5s
retries: 0
start_period: 1m
restart: unless-stopped
# NFS server for hosting the OS files
pi-nfs:
image: andrewkidd/nfs-server
container_name: pi-nfs
privileged: true
profiles:
- netboot
- debian-netboot
env_file:
- .env
volumes:
- ./README.md:/mnt/nfsshare/test.txt:ro
- netboot-os-data:/mnt/nfsshare
ports:
- 111:111
- 111:111/udp
- 2049:2049
- 2049:2049/udp
- 32765-32768:32765-32768
- 32765-32768:32765-32768/udp
healthcheck:
test: ["CMD", "test", "-f", "/mnt/nfsshare/test.txt"]
interval: 1m
timeout: 5s
retries: 0
start_period: 1m
restart: unless-stopped
volumes:
netboot-boot-data:
netboot-os-data:

Then run the dockerfile

Terminal window
cd ~
git clone https://github.com/andrewiankidd/project-iluvatar.git
cd project-iluvatar
# Load core NFS kernel module on the host (needed so the kernel exposes NFS facilities used by the server container)
sudo modprobe nfs
# Load the kernel NFS server module (the container orchestrates exports, but the host kernel actually serves them)
sudo modprobe nfsd
# Free TCP/UDP 111 and related RPC ports so the containerized NFS server can bind cleanly
sudo systemctl stop rpcbind
sudo systemctl stop rpcbind.socket
# Start the TFTP + NFS (+ HTTP for configs) stack in containers
sudo docker compose -f ./src/bootstrap/netboot/docker-compose.yml up

The TFTP and NFS servers will now be running 🎉

Now the TFTP server is running, we need to make it known to your local network. In order to do this you must update your DHCP server configuration.

You can get really advanced with this - so I will defer to the linuxserver.io documentation for more info should you want to research more.

But in my testing, I found that all the Pi really cares about is OPTION 66, which defines your provision server, which in our case is our TFTP server.

So with that in mind, I did the following:

  1. SSH’d to my router…
    Terminal window
    ssh root@192.168.1.1
  2. opened dnsmasq.conf…
    Terminal window
    nano /etc/dnsmasq.conf
  3. appended the following lines (where 192.168.1.66 is the IP of the machine running Docker):
    /etc/dnsmasq.conf
    # define option 66 / tftp-server
    dhcp-option=66,192.168.1.66
  4. restarted dnsmasq
    Terminal window
    /etc/init.d/dnsmasq restart

You should be able to test this change with an nmap dhcp discovery request

Terminal window
root@ubuntuvm:~$ nmap --script broadcast-dhcp-discover
Starting Nmap 7.80 ( https://nmap.org ) at 2000-01-01 00:00 UTC
Pre-scan script results:
| broadcast-dhcp-discover:
| Response 1 of 1:
| IP Offered: 192.168.1.249
| DHCP Message Type: DHCPOFFER
| Server Identifier: 192.168.1.1
| IP Address Lease Time: 2m00s
| Renewal Time Value: 1m00s
| Rebinding Time Value: 1m45s
| Subnet Mask: 255.255.255.0
| Broadcast Address: 192.168.1.255
| Router: 192.168.1.1
| Domain Name: kidd.local
| Domain Name Server: 192.168.1.3, 192.168.1.1
|_ TFTP Server Name: 192.168.1.66\x00
WARNING: No targets were specified, so 0 hosts scanned.
Nmap done: 0 IP addresses (0 hosts up) scanned in 3.60 seconds

As you can see on the last line of the dhcp script output, the TFTP server is now defined and is the IP of the machine running Docker 🎉

If you power up your Pi again, you should see it query the TFTP server for files (TFTP 1: file not found) Capture of a Raspberry Pi bootloader attempting to read files from TFTP

So now we have a Pi looking to boot from network, and a Server looking to fulfill that need.

Next we need to provide the Boot + OS files to the server!