Raspberry Pi Zero W with the root FS mounted over NFS
I like to run my Raspberries with the root FS mounted remotely (eg: over NFS, and this is easy to do when the machine has an ethernet socket and a wired connection.
Doing the same thing over wireless, however, is not so simple, so here's a guide to what you need to do.
Note: whether "NFS root over wireless" is a sensible thing to do or not, I don't know until I've been able to try it for a while, so I'll post an update here (and edit this comment) once I've had a chance to try it out in practice.
Overview
To get the root file system mounted over NFS across a wireless link, you need to do the following things(not necessarily in sequence, but they all have to be in place at the end for the whole thing to work):
- create an initramfs for the Pi Zero to use at boot time
- configure the Pi Zero to boot using the initramfs
- this is necessary so that you can…
- set up wpa_supplicant (in the initramfs) to connect to the wireless network
- ensure that all files necessary for the wireless interface hardware to work are present in the initramfs
- put the root FS image you want to run from onto an NFS server
- (you might need to set up an NFS server too, but that's outside the scope of this discussion - if you need to find out how to set up an NFS server, there are plenty of articles on the Internet to help you)
- configure the Pi Zero to expect its root FS to be on NFS
I was surprised that I couldn't find an article like this already on the Internet, explaining how to do this. There were several helpful documents which either encouraged me to try (suggesting that it was at all possible), or pointed me at different steps in the right direction, but nobody seemed to have written up "how to start from a Pi Zero W and a standard Raspbian image, and end up with an NFS-mounted root FS over wireless". So, here we are.
The instructions assume that you want to end up running Raspbian Stretch on the Pi Zero W.
Some of the articles I found useful along the way were:
- Getting the Raspberry Pi to use an initramfs: https://www.raspberrypi.org/forums/viewtopic.php?f=63&t=10532
- Getting the wireless interface configured from the running initramfs: https://ubuntuforums.org/showthread.php?t=1853554
- Creating an initramfs: https://manpages.debian.org/jessie/initramfs-tools/initramfs-tools.8.en.html
I also found a very interesting article about running a Raspberry Pi Zero without an SD card at all, which I hadn't even believed to be possible: https://dev.webonomic.nl/how-to-run-or-boot-raspbian-on-a-raspberry-pi-zero-without-an-sd-card
Some people have even created Pi Zero clusters, booting Pies without SD cards involved: https://www.raspberrypi.org/forums/viewtopic.php?f=49&t=199994
Those last articles didn't really help with my objective of running root NFS over wireless, but I'm leaving them here as placeholders just in case you (or I, at some later date) find them useful anyway.
Note that some of the steps explained below are best run on the Raspberry Pi Zero itself (or at least, on a Raspberry Pi running the exact version of Raspbian which you are trying to end up with), so it can be a good start to download Raspbian and copy it to an SD card which you can then boot your Pi Zero from (unless you already have a working Pi Zero, in which case simply use that).
Perform all the following steps as root.
Personally I recommend just doing 'sudo bash' at the start.
So, step by step…
Configuring and creating the initramfs
Creating an initramfs is very easy under Raspbian, and this is one of the steps best done on the Pi Zero itself.
All the tools you need should already be installed (because they get used whenever you update certain packages on the machine), so you just need to edit the configuration files and then build the initramfs.
Configuration files
These are found under /etc/initramfs-tools and need adjusting or creating as follows:
- Modify initramfs.conf to contain:
MODULES=netboot (instead of MODULES=most) BOOT=nfs (instead of BOOT=local) DEVICE=wlan0
- Modify modules and place at the end:
brcmfmac brcmutil cfg80211 rfkill
- Create hooks/nfsroot (you can actually name it anything you like, so long as it's a file under hooks) with the content:
- nfsroot
#!/bin/sh PREREQ="" prereqs() { echo "$PREREQ" } case $1 in prereqs) prereqs exit 0 ;; esac . /usr/share/initramfs-tools/hook-functions copy_exec /bin/grep /bin copy_exec /sbin/lsmod /sbin copy_exec /sbin/ifconfig /sbin copy_exec /sbin/wpa_supplicant /sbin tail -n+4 /etc/wpa_supplicant/wpa_supplicant.conf >${DESTDIR}/etc/wpa_supplicant.conf cp /lib/firmware/brcm/brcmfmac43430-sdio.txt ${DESTDIR}/lib/firmware/brcm mkdir -p ${DESTDIR}/var/run/wpa_supplicant
- This step assumes that you already have a file /etc/wpa_supplicant/wpa_supplicant.conf on your running system, containing something like:
- wpa_suppliant.conf
ctrl_interface=DIR=/var/run/wpa_supplicant GROUP=netdev update_config=1 network={ ssid="Notwork" psk=5ec8def3d30ba1487e2308e55fc81cc8ea82253c0fd850f0d2b3d06f81d03014 }
- See my notes on getting started with the Pi Zero W regarding how to create this file, if you need to.
- Create scripts/nfs-top/wifi (again, call it whatever you like, so long as it lives in scripts/nfs-top):
- wifi
#!/bin/sh PREREQ="udev" prereqs() { echo "$PREREQ" } case $1 in prereqs) prereqs exit 0 ;; esac if grep -q splash /proc/cmdline; then /bin/chvt 1 fi sleep 3 if grep -q splash /proc/cmdline; then /sbin/usplash -c & sleep 1 fi /sbin/wpa_supplicant -Dwext -iwlan0 -c /etc/wpa_supplicant.conf & sleep 5 ipconfig wlan0 &
Build the initramfs
Once the above initramfs configuration files are in place, you can build the initramfs itself with the command:
- update-initramfs -c -k all
The output will be something similar to:
update-initramfs: Generating /boot/initrd.img-4.14.62+ W: Possible missing firmware /lib/firmware/brcm/brcmfmac4373-sdio.bin for module brcmfmac W: Possible missing firmware /lib/firmware/brcm/brcmfmac4356-sdio.bin for module brcmfmac W: Possible missing firmware /lib/firmware/brcm/brcmfmac43430a0-sdio.bin for module brcmfmac W: Possible missing firmware /lib/firmware/brcm/brcmfmac43341-sdio.bin for module brcmfmac W: Possible missing firmware /lib/firmware/brcm/brcmfmac4373.bin for module brcmfmac
The script creates an initramfs file in /boot with a name as shown above (which depends on the current kernel at the time you obtained or last upgraded your copy of Raspbian).
That's all there is to building an initramfs - it's quite a complicated thing, but the update-initramfs script makes it very simple to build.
Note: if you encounter any documentation explaining how to build an initrd, walk slowly away and find something more up-to-date. initrd was an earlier method of achieving the same thing as initramfs, but the tools involved are different, and no current release of Raspbian will use an initrd.
The update-initramfs script leaves the file in the correct place to be used during the boot process, however you should note the exact filename it tells you it's creating, because you'll need it in the next step.
Configure the Pi Zero to load the initramfs during the boot sequence
Add the following to the end of /boot/config.txt on the SD card you'll be booting the Pi Zero from:
# Load an initrd initramfs initrd.img-4.14.62+ followkernel
Just make sure the filename corresponds to whatever update-initramfs created for you above.
That's it.
Make the root FS available from an NFS server
There are several ways of going about this, but I would recommend:
- Download Raspbian Stretch, either onto your NFS server, or onto any machine which can write to the NFS server over a network
- Loopback mount the 'root' partition from the image (you can use kpartx to expose the partitions from inside the image, and just mount the one you want)
- Copy the files in the 'root' partition into a suitable directory which can be exported from the NFS server
If you already have a working Pi Zero W (perhaps with installed and configured applications, which you'd like to keep), then instead of the above:
- Shut down the Pi and remove the SD card
- Put the SD card into your NFS server, or another machine which can write to the NFS server over a network
- Mount the 'root' partition and copy the files from it into a directory which can be exported from the NFS server
Whichever way you go about doing this, make sure you copy the files with the correct numeric User IDs and Group IDs (for example, using rsync with the --numeric-ids option). It is essential that the files in the image have the ownerships corresponding to those in /etc/passwd in the Raspberry image, and not those on your NFS server.
Once you've got the files you want in a suitable directory, add it to /etc/exports on the NFS server (or however else you configure what your server exposes to its clients):
/srv/PiZeroW 192.168.0.0/16(rw,async,no_subtree_check,no_root_squash,no_all_squash)
Configure the Pi Zero to mount its root FS over NFS
/boot/cmdline.txt is the file which tells the Raspberry how to start up.
As standard, it probably contains something very close to:
dwc_otg.lpm_enable=0 console=serial0,115200 console=tty1 root=PARTUUID=c7928a26-02 rootfstype=ext4 elevator=deadline fsck.repair=yes rootwait
To start with the root FS mounted over NFS, change this to:
dwc_otg.lpm_enable=0 smsc95xx.turbo_mode=N console=serial0,115200 console=tty1 root=/dev/nfs nfsroot=192.168.36.11:/srv/PiZeroW,tcp rw ip=dhcp rootfstype=nfs elevator=deadline fsck.repair=yes rootwait
Obviously (?), change the IP address to that of your NFS server, followed by the pathname of the NFS exported directory which contains your desired root file system.
Note that if your NFS server itself is running Debian Stretch, or a similarly recent version of NFS, you probably need to add ,vers=3 to the nfsroot= parameter:
dwc_otg.lpm_enable=0 smsc95xx.turbo_mode=N console=serial0,115200 console=tty1 root=/dev/nfs nfsroot=192.168.36.11:/srv/PiZeroW,tcp,vers=3 rw ip=dhcp rootfstype=nfs elevator=deadline fsck.repair=yes rootwait
Alternatively, depending on your DHCP server...
The above cmdline.txt file specifies where to find the NFS server and which share to mount for the root FS. Obviously, if you have several Pies on your network, you'll want to make sure they each mount different shares.
If you're running ISC's DHCP server (or another which can do the same thing), you can change the cmdline.txt file to just:
dwc_otg.lpm_enable=0 smsc95xx.turbo_mode=N console=serial0,115200 console=tty1 root=/dev/nfs ip=dhcp rootfstype=nfs elevator=deadline fsck.repair=yes rootwait
and then in your dhcpd.conf on the DHCP server specify:
option root-path = concat("192.168.36.11:/srv/", binary-to-ascii(16, 8, ":", substring(hardware,1,6)));
This specifies the IP address of the NFS server, but then provides the name of the share to mount for the root FS based on the MAC address of the Raspberry Pi. Obviously this is going to be different for each one on your network, so with a single generic cmdline.txt on every SD card, you can boot multiple Pies without them all trying to mount the same file system.
The NFS shares should be named /srv/b8:27:eb:25:92:5b etc, according to the MAC addresses of your particular Pies. Note that any leading zeros should be omitted from the share name, so if the MAC address of your Pi is b8:27:eb:05:02:0b you would need to have a share named /srv/b8:27:eb:5:2:b.
Finally
After following all the above steps with the SD card installed in the Raspberry Pi, simply reboot and watch the system start up, load its initramfs, configure the wireless interface and obtain an IP address, then mount the root FS over NFS and finally end up at the familiar command prompt:
Raspbian GNU/Linux 9 raspberrypi tty1 raspberrypi login:
Closing comments
I haven't yet started from a fresh SD card and followed the above steps in order to make sure they are complete (and I have a strong suspicion that while they work, a few bits might be redundant, especially the root=/dev/nfs entry in /boot/cmdline.txt and the BOOT=nfs and DEVICE=wlan0 entries in initramfs.conf), so I plan to go through them and check that they are complete (and, as far as possible, not over-complicated), and once that is done, this comment will be removed (or at least edited).
Go up
Return to main index.