Network Booting The Pi 4

We’ve talked about PXE booting the Raspberry Pi 3B+, and then looked at the Raspberry Pi 4 as a desktop replacement. But there’s more! The Pi 4 sports a very useful new feature, the flashable bootloader. Just recently a beta version of that bootloader was released that supports PXE  — booting up over the network — which has become a must-have for those of us who have had consistently bad experiences with root filesystems on SD cards.

Pi with no SD CardWhat are the downsides, I hear you ask? You might see slower speeds going across the network compared to a high quality SD card, particularly with the Pi 4 and its improved SD card slot. PXE does require an Ethernet cable; WiFi is not enough, so you have that restriction to contend with. And finally, this isn’t a portable option — you are tethered to that network cable while running, and tethered to your network to boot at all.

On the other hand, if you’re doing a permanent or semi-permanent install of a Pi, PXE is absolutely a winner. There are few things worse than dragging a ladder out to access a Pi that’s cooked its SD card, not to mention the possibility that you firewalled yourself out of it. Need to start over with a fresh Raspbian image? Easy, just rebuild it on the PXE server and reboot the Pi remotely.

Convinced PXE is for you? Let’s get started!

To boot a Raspberry Pi 4 using PXE, there are a few steps required, starting with updating that bootloader firmware. This means installing Raspbian to an SD card and booting the Pi off of it at least once. From there, we turn to the PXE server to build the remote filesystem and set up the NFS and dnsmasq services. This article draws from a pair of official Pi network boot guides.

Bootloader Update

At the time of writing, the eeprom firmware that supports PXE boot is still in beta. We have to grab that firmware, change the boot order configuration, and then flash it to the onboard chip. Once the Pi 4 is booted off your Raspbian SD card, we can do the following to get the firmware updated:

sudo apt-get update
sudo apt-get upgrade
rpi-eeprom-config pieeprom-2019-10-16.bin > bootconf.txt
sed -i s/0x1/0x21/g bootconf.txt
rpi-eeprom-config --out pieeprom-2019-10-16-netboot.bin --config bootconf.txt pieeprom-2019-10-16.bin
sudo rpi-eeprom-update -d -f ./pieeprom-2019-10-16-netboot.bin
cat /proc/cpuinfo

That last command should output some information on the Pi itself. We’re interested in the entry for the Pi’s serial number. Take note of the last 8 characters of that code, as we’ll use it later. In my case, it is “0c4c21e5”. That’s all the setup needed for the Pi itself.

Building the Filesystem

Last time we set up a PXE server, we used Centos and a dedicated network. This time, let’s use Debian, and see if we can get PXE working on an existing home network. These instructions should apply for a Raspbian server as well.

The following simply builds the Pi’s filesystem as an NFS share. The Raspbian image we’re using does need two files manually updated, which is why we’re deleting and re-downloading start4.elf and fixup4.dat

sudo apt-get install unzip kpartx dnsmasq nfs-kernel-server
sudo mkdir -p /nfs/raspi1
unzip raspbian_latest
sudo kpartx -a -v 2019-09-26-raspbian-buster.img
mkdir rootmnt
mkdir bootmnt
sudo mount /dev/mapper/loop0p2 rootmnt/
sudo mount /dev/mapper/loop0p1 bootmnt/
sudo cp -a rootmnt/* /nfs/raspi1/
sudo cp -a bootmnt/* /nfs/raspi1/boot/
cd /nfs/raspi1/boot
sudo rm start4.elf
sudo rm fixup4.dat
sudo wget
sudo wget

Remember that serial number from earlier? Here’s where it comes into play. The first place a Pi attempts to PXE boot from is a folder named the last 8 characters of that serial number. You’ll want to replace each “0c4c21e5” below with the serial number you found earlier. We’re also using a bind mount so the /boot folder is accessible as part of the tftp service, as well as being mounted as part of the NFS share. The advantage here is that the kernel and rest of the boot code can be updated using apt.

sudo mkdir -p /tftpboot/0c4c21e5
echo "/nfs/raspi1/boot /tftpboot/0c4c21e5 none defaults,bind 0 0" | sudo tee -a /etc/fstab
sudo mount /tftpboot/0c4c21e5
sudo chmod 777 /tftpboot

We’ll create a file in the boot folder to enable SSH, modify the Pi’s fstab so it won’t look for filesystems on the SD card, and finally replace the boot command in cmdline.txt. Be sure to replace nfsroot IP address with the IP of the Debian machine serving as the PXE server.

sudo touch /nfs/raspi1/boot/ssh
sudo sed -i /UUID/d /nfs/raspi1/etc/fstab
echo "console=serial0,115200 console=tty root=/dev/nfs nfsroot=,vers=3 rw ip=dhcp rootwait elevator=deadline" | sudo tee /nfs/raspi1/boot/cmdline.txt

Configuring Services

Setting up the NFS share is as easy as adding a line to /etc/exports and starting the services. Do note that we’re not setting up a particularly secure NFS service. This isn’t a problem on a home network where you trust all the computers, but don’t run this setup exposed to the public internet.

echo "/nfs/raspi1 *(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

We need to add our settings to the dnsmasq config file, which is where most of the magic happens. Let’s talk about that “proxy” setting. What we’re asking dnsmasq to do is watch for DHCP requests, and rather than respond to those requests directly, wait for the primary DHCP server to assign an IP address. If dnsmasq sees a request for PXE information, it will send additional information to inform the PXE-capable device of the PXE server information. The upside is that this approach lets us support PXE booting without modifying the primary DHCP server.

You will need to adjust the dhcp-range setting to match your network. Usually it can be set to the broadcast address of your network, which can be identified using ip addr, looking at the brd entry.

echo 'dhcp-range=,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=/tftpboot' | sudo tee -a /etc/dnsmasq.conf
echo 'pxe-service=0,"Raspberry Pi Boot"' | sudo tee -a /etc/dnsmasq.conf


The final step is to start dnsmasq and watch the log for connections.

sudo systemctl enable dnsmasq
sudo systemctl restart dnsmasq
sudo tail -f /var/log/daemon.log

At this point, you can take the Pi 4 we configured, remove the SD card, and attempt booting it over PXE. You should be able to see the tftp requests for boot files in the log, and finally an NFS mount request. Congratulations, we’ve made it work!

44 thoughts on “Network Booting The Pi 4

  1. The formatting of this article shows that the site goofed again. There are words broken when it should be sentences. The command strings are broken on to two lines when they should nee all on one line.

      1. Yes it does. And I saw that my comment needed work. It seems this keyboard goofed. Normal for it. It’s a USB keyboard from the early part of the century, and this laptop is new….

  2. “compared to a high quality SD card, particularly with the Pi 4 and its improved SD card slot.”
    What has been improved with the SD card performance on the Pi 4? Searching this online has turned up short

  3. Afaik apt-get is old, you should use apt instead. No need for sudo when using wget and personally i would check the SHA256 (or at least SHA1) of the bootloader before flashing anything.

    1. Fair point about checking the SHA256. apt-get and apt seem to be equally recommended in the current Debian documentation. We need to use “sudo wget” because we’re replacing files in the boot folder, which is all owned by root.

      1. Ok for wget, did not notice that.
        For apt-get/apt, you are right. apt-get is older but i can’t find any sign that it’s no longer supported. apt is newer and easier to use apparently. See for example . Well, i’m not a Linux-Guru, i just repeated what i read on some forum somewhere… Next time i will check before repeating… I will stick with apt, it’s shorter…

    2. for what its worth, I find some vendors that release custom linux distributions only freeze their packages using apt-get. In other words, I’ve found that apt can brick some embedded devices since apt and apt-get dont seen to share the same package freeze db. Looking at you Gemini computer. Of course, this isn’t a reason to use one over the other…but it is a word of caution.

    1. Maybe one of these days I’ll figure out how to run a dual-head setup and write that up. 1 Pi for two workstations. It has plenty of CPU power for it, if you’re just doing educational software.

    2. Oh, is it meant to have dual screens? I thought they put on two micro HDMI connectors so that there would be a spare when the first one breaks.

      I haven’t broken one (yet), but it doesn’t strike me as the most robust connector.

  4. Next steps:

    – Set a second Pi as PXE server for the first Pi; Put PXE boot on it
    – Set a third Pi as PXE server for the second Pi; Put PXE boot on it
    while(true) {
    – Set a ${N} Pi as PXE server for the ${N – 1} Pi; Put PXE boot on it
    – N++

    Jokes aside, my “permanent pi” has a 128MB SD card with the boot partition only, and the root partition is on a USB HDD…

    1. So here’s the funny thing, once the Pi boots, you can connect the wifi to the same network, manually remove the Ethernet route, and run the nfs root over WiFi. It’s not super reliable, but does work.

  5. Hi, Have managed to netowrk boot, except on mine:
    systemctl status rpi-eeprom-update.service
    the service doesn’t start during bootup.
    my fstab is:

    proc /proc proc defaults 0 0 /boot nfs defaults,vers=3 0 0

    my cmdline.txt is:

    selinux=0 dwc_otg.lpm_enable=0 console=tty1 rootwait rw nfsroot=,v3 ip=dhcp root=/dev/nfs elevator=deadline modprobe.blacklist=bcm2835_v4l2

    is the problem with them? or something else.
    Thanks for your how to, very helpful

    1. It looks right. If it’s booting, then the PX stuff is probably all correct. I would troubleshoot the service not starting as being unrelated to network booting.

      I assume you’ve done the basics, like install updates, disable and re-enable the service, etc.

  6. This reminds me of the LTSP project ( plus use of EtherBoot (

    I have no practical experience here mind you, but could it be possible to bring the WiFi link up early enough during boot-up to somehow redirect the PXE generated ethernet frames over an EtherIP tunnel ( using the WiFi link to a gateway device that de-cap’s and forwards the ethernet frame?

    I’m guessing someone will reply that my idea won’t work given PXE is a BIOS function and not geared for this, but just curious if something similar to the kexec comment someone made could make an equivalent to PXE viable, but without installing a bunch of ethernet/WiFi bridge devices.

    Is it possible to virtualize Raspbian within Raspbian and still retain all the hardware access features? For example, take the earlier super minimal image idea someone mentioned and make that your wireless bridge; booting the 2nd image as a VM with a software defined VLAN between it and the minimal image. A quick search suggests PXE on VMware VMs may be possible at least (

    Again, no experience here. Just enjoyed the article and throwing out ideas and questions.

      1. Assuming iPXE will work with RPi, then I think the iPXE bootchaining option looks promising. Assuming the Pi4 DHCP request options are the same as the Pi3 (, configure the DHCP server to serve up the iPXE rom for this vendor class (other options may be required or need to be tested):

        Option 60 (vendor-class-identifier) [sic] – needs to be set to text value “PXEClient”

        Then setup the DHCP server to serve up the stuff in the article for this user class:

        Option 77 (user-class) – needs to be set to text value “iPXE”

        Anyway, this looks promising to me if iPXE is compatible, since it supports WiFi booting. Note: the Pi3 thread above quotes option 60 as vendor-class-identifier; however, IANA lists that as simply “class id”, listing option 124 as “Vendor-Identifying Vendor Class” instead. Just pointing out the name of the DHCP option in that Pi3 solution may be incorrectly referenced, but assume the option listed was what was successfully used.

        Hope this helps someone with a Pi4 take the next steps.

  7. Hi Jonathan,

    I’m trying to get PXE working for testing with no success. I read some articles/tutorials and the setup mostly is RPi (server) to RPi (client). With this, can I just ask if PXE will work if the setup is RPi 4 (client) to PC with Rasbian installed (server)? Sorry for newbie question. I’m trying to learn as well.

    Thanks for putting this article.


      1. Yup, Debian on the PC. Thanks for correcting me.

        Okay. I’ll try again. I already thought of getting another RPi4 for testing in case it’s not supported. I hope to figure out what’s missing on my setup/config.

        Thanks for the feedback, Jonathan.

        All the best!

  8. For anyone attempting this and hanging for about a minute after acquiring an IP address (during which time the green LED periodically flashes) before getting the following messages:

    VFS: Unable to mount root fs via NFS, trying floppy.
    VFS: Cannot open root device “nfs” or unknown-block(2,0): error -6
    Please append a correct “root=” boot option; here are the available partitions:
    Kernel panic – not syncing : VFS: Unable to mount root fs on unknown-block(2,0)

    followed by a kernel panic, this may be caused by the Pi attempting to mount the NFS share via UDP when the server is configured to only accept TCP connections (the default for the CentOS 8 nfs-utils package). There are two possible solutions: either add udp=y under the [nfsd] section in /etc/nfs.conf, or (preferably) add proto=tcp to the nfsroot= options in /boot/cmdline.txt

    Hope that helps!

  9. Thank you!
    I just reproduced this using a Pi2 as the server. Interesting setup. Cleaner than the Pi-Desktop, allowing real usage.
    I’m going to try to set this up for a motionEyeOS setup to obviate the need for the SDCard boot.
    So many possibilities.
    I would suggest something larger than a 16GB storage device on the server, especially if you are going to have multiple machines served off of it.

  10. Additionally, Raspbian now supports NFSv4.1 (NFSv4.2 is not supported yet), which resulted in a 15 second reduction in boot time for me. Here is my current cmdline.txt:

    console=serial0,115200 console=tty root=/dev/nfs nfsroot=,vers=4.1,proto=tcp rw ip=dhcp rootwait elevator=deadline

  11. One more gotcha: if the version of Linux you are using has SELinux enabled, you will need to set the appropriate context for the tftp files. You can do this with the following command:

    sudo chcon -R unconfined_u:object_r:tftpdir_rw_t:s0 /path/to/boot/dir

  12. I don’t know what changed, but something did.
    RPi2, Raspbian Lite base for TFTP server.
    RPi4, updated to latest beta bootloader, and downgraded back to the 10/16/19.
    Imported the Raspbian Latest, followed all the instructions above. (as I did earlier this month)
    In log on Pi2

    Jan 23 12:10:24 rpi2-tftp dnsmasq-tftp[2184]: error 0 Early terminate received from
    Jan 23 12:10:24 rpi2-tftp dnsmasq-tftp[2184]: failed sending /tftpboot/2ba2d181/start4.elf to
    Jan 23 12:10:24 rpi2-tftp dnsmasq-tftp[2184]: sent /tftpboot/2ba2d181/config.txt to
    Jan 23 12:10:27 rpi2-tftp dnsmasq-tftp[2184]: sent /tftpboot/2ba2d181/start4.elf to
    Jan 23 12:10:27 rpi2-tftp dnsmasq-tftp[2184]: sent /tftpboot/2ba2d181/fixup4.dat to
    Jan 23 12:24:02 rpi2-tftp dnsmasq-tftp[2184]: error 0 Early terminate received from
    Jan 23 12:24:02 rpi2-tftp dnsmasq-tftp[2184]: failed sending /tftpboot/2ba2d181/start4.elf to
    Jan 23 12:24:02 rpi2-tftp dnsmasq-tftp[2184]: sent /tftpboot/2ba2d181/config.txt to
    Jan 23 12:24:05 rpi2-tftp dnsmasq-tftp[2184]: sent /tftpboot/2ba2d181/start4.elf to
    Jan 23 12:24:05 rpi2-tftp dnsmasq-tftp[2184]: sent /tftpboot/2ba2d181/fixup4.dat to
    pi@rpi4-1:~ $ sudo rpi-eeprom-update
    BCM2711 detected
    BOOTLOADER: update required
    CURRENT: Wed 16 Oct 2019 05:00:03 PM UTC (1571245203)
     LATEST: Fri 17 Jan 2020 05:37:11 PM UTC (1579282631)
    VL805: up-to-date
    CURRENT: 000137ad
     LATEST: 000137ad

    Any ideas?

    1. Updated /boot/cmdline.txt to
      console=serial0,115200 console=tty root=/dev/nfs nfsroot=,vers=4.1,proto=tcp rw ip=dhcp rootwait elevator=deadline

      Jan 23 12:43:08 rpi2-tftp dnsmasq-tftp[400]: sent /tftpboot/2ba2d181/start4.elf to
      Jan 23 12:43:08 rpi2-tftp dnsmasq-tftp[400]: sent /tftpboot/2ba2d181/fixup4.dat to
      Jan 23 12:43:41 rpi2-tftp systemd[1]: dev-serial1.device: Job dev-serial1.device/start timed out.
      Jan 23 12:43:41 rpi2-tftp systemd[1]: Timed out waiting for device /dev/serial1.
      Jan 23 12:43:41 rpi2-tftp systemd[1]: Dependency failed for Configure Bluetooth Modems connected by UART.
      Jan 23 12:43:41 rpi2-tftp systemd[1]: hciuart.service: Job hciuart.service/start failed with result 'dependency'.
      Jan 23 12:43:41 rpi2-tftp systemd[1]: dev-serial1.device: Job dev-serial1.device/start failed with result 'timeout'.
      Jan 23 12:43:41 rpi2-tftp systemd[1]: Reached target Multi-User System.
      Jan 23 12:43:41 rpi2-tftp systemd[1]: Reached target Graphical Interface.
      Jan 23 12:43:41 rpi2-tftp systemd[1]: Starting Update UTMP about System Runlevel Changes...
      Jan 23 12:43:42 rpi2-tftp systemd[1]: systemd-update-utmp-runlevel.service: Succeeded.
      Jan 23 12:43:42 rpi2-tftp systemd[1]: Started Update UTMP about System Runlevel Changes.
      Jan 23 12:43:42 rpi2-tftp systemd[1]: Startup finished in 4.751s (kernel) + 1min 32.394s (userspace) = 1min 37.146s.

      and hangs.

      1. Thanks. I’ve built it 3 times today, clean installs, on a Pi2 and a PI3 Rasbian Buster Lite & fully updated. Same result each time.
        Currently building a Debian Buster VBox image. It’s using 4 processors, 4GB RAM, 50GB HD. I will also try an Ubuntu 16.04 image in a bit, to confirm not a RASPBIAN issue.

          1. That fixed it, thanks!
            Now to auto-install a RAMDisk (RPi4-4GB) and add NAS connection.
            There are some advantages to this over PiServer, but I think both are going to be in my tool box.

Leave a Reply

This site uses Akismet to reduce spam. Learn how your comment data is processed.