How to netboot a Raspberry Pi 3 from a Linux server with TFTP and NFS
31 January 2018
You can find plenty of guides to booting a Raspberry Pi from the network, but the one’s I’ve seen make one or two assumptions. One is that the official guide assumes that you’re booting off of another Raspberry Pi, which you use to generate the boot folder. Another is that you’ll be providing DHCP and TFTP on the same server, e.g. using
dnsmasq. This is annoying because you end up running two DHCP servers on the same network, and it has the potential to interfere with the network configuration on the server that’s running it.
So here’s my plan. I’ve already got a Tomato router (running Advanced Tomato), and it’s got
dnsmasq as a DHCP server. So in theory I should be able to direct the Pi to my real server, which I’m going to run tftpd-hpa on. The same server also runs my NFS mounts, so I’ll use it to host the root directory for the Pi as well.
I expected this to be simple, but it turned out to be rather hard. There are a lot of moving pieces, and many of them don’t give you much help when they break. So I’m documenting the process here.
What you need to do this:
- A server to host NFS and TFTP
- A router with a configurable DHCP daemon, e.g.
dnsmasq(Tomato-based routers are great for this.)
- A Raspberry Pi 3 (the first model to support netbooting without an SD card) with a working Raspbian installation on an SD card
- A working NFS setup (not hard to do, but not described here)
- Possibly a lot of patience.
The first, and apparently most difficult part, is just getting a working TFTP server. I installed
tftpd-hpa on Ubuntu 17.10, made sure port
69 was unblocked, did some basic configuration. No luck, even over localhost. After some mucking about, I could tell using
-v -v -v with
tftpd was receiving the request, but it never bothered to respond—-and it didn’t say why. After hours of trying different suggestions online, I managed to get a working configuration with the following:
TFTP_USERNAME="tftp" TFTP_DIRECTORY="/path/to/boot/" TFTP_ADDRESS=":69" RUN_DAEMON="yes" TFTP_OPTIONS="--secure -p"
I’m not sure if the
-p is needed, but honestly I’m too scared to change anything now. Some of these settings are almost designed to cause frustration.
--secure isn’t exactly a security setting, it sets the “default” directory to
TFTP_DIRECTORY and translates all addresses to subdirectories of that one. Yes, that’s right, without
--secure, a request for a relative path won’t work. My
/boot files are owned by
umask=022, and that seems to work fine.
The main thing you need to do for tftp is set the directory to the path you want to use for the Pi’s boot files. Since I have the NFS server and the TFTP server on the same hardware, I can conveniently point the TFTP server at
/boot in the Pi’s root directory under NFS.
But first things first, we have to get the root directory onto the server first. The official guide is rather useless here. It assumes that we’re going to run the server off of another Pi, which we can conveniently copy the root from. I went the opposite direction. I set up the NFS server with the options
and, mounting the folder on the client Pi (with Raspbian installed to an SD card), copied the root with
rsync to the NFS folder. Conveniently enough, we can use the exact same folder and settings to point the Pi to later. (Having virtually no security on the root folder of the Pi’s filesystem is pretty concerning, however. I haven’t figured out how to get around this given the limitations of PXE booting on the Pi.)
Alright, so I now have a working Raspbian filesystem served over NFS, and I can point my TFTP server to
/boot as previously described. I do need to edit
/boot/cmdline.txt as described in the official guide to point the boot code towards the NFS server. Also best to edit
/etc/fstab and remove everything but
/proc, otherwise Raspbian thinks the boot didn’t succeed.
# cmdline.txt dwc_otg.lpm_enable=0 console=serial0,115200 console=tty1 root=/dev/nfs nfsroot=10.0.1.102:/path/to/root,vers=3 rw ip=dhcp rootwait elevator=deadline
The bit starting with “root” is the important part. We need to point the boot code to the directory with the Pi’s root on the NFS server, as shown.
The remaining task is to get the DHCP server on the network to respond to the PI’s PXE discovery request. I fought
dnsmasq on Advanced Tomato for a long time, and finally settled on setting the appropriate DHCP fields by hand, as established in RFC 2132.
dhcp-option=43,Raspberry Pi Boot dhcp-option=66,10.0.1.102
Where the IP address belongs to the TFTP server. There are three spaces after “Boot” as suggested in the official guide, but I don’t know if they’re actually necessary. Surprisingly enough, these two options alone were enough to get it working—-I just set them on the DHCP page of Advanced Tomato.
The last step is just to make sure booting over ethernet is enabled on the Pi, as the official guide says. After that, removing the microSD card and rebooting, everything should just work!