Raspberry Pi NFS Root
Raspberry Pi can be diskless booted using multiple methods.
This approach uses an SD card for loading the Linux kernel, the /boot/firmware partition is loaded from SD card. Then /boot/firmware/cmdline.txt sets the kernel to boot from NFS rather than use the local disk. Reverting back to SD Card boot is trivial as only the cmdline.txt file needs to be changed.
Advantages
- Only external dependency is the NFS server. DHCP, bootp, tftp servers are not required.
- It's trivial to revert the RPi from NFS root to local boot by just changing the
cmdline.txtfile.
Installation
Environment Setup
This setup uses RPi model: 4B+ on Debian Bookworm.
root@rpi:~ # lsb_release -a
No LSB modules are available.
Distributor ID: Debian
Description: Debian GNU/Linux 12 (bookworm)
Release: 12
Codename: bookworm| Host Name | IP | Description |
|---|---|---|
| alpine-nfs-server | 10.13.1.231 | NFS server |
| rpi | 10.13.1.252 | NFS client |
Step 0: Clean Install RPi on SD Card
Follow the official documentation and install Raspbian, Debian, Ubuntu or the Linux flavor of your choice. Use a suitably size SD card and perform local installation of the OS to the SD card.
Step 1: Setup NFS Server
Follow the alpine-nfs documentation to setup an NFS server in the network. The nfs server root must be 10GB or greater and should export /export/rpi as the NFS mount point which will be exported from the server.
Step 2: Transfer SD Card Root to NFS
In the RPi, mount the nfs export locally and copy/rsync the root file system files from SD card to the NFS mount point.
rpi:~# mount -t nfs 10.31.1.231:/export/rpi /mnt
# Run "mount" and confirm that NFS export is mounted on /mnt
# Run rsync multiple times to ensure a clean copy of the rootfs if copied to NFS
rpi:~# rsync -ax / /mnt/
rpi:~# rsync -axv --delete / /mnt/
rpi:~# rsync -axv --delete / /mnt/
# While rsync is running, on alpine-nfs-server watch the progress
alpine-nfs-server:~# watch -n1 du -sh /export/rpi
Step 3: Change Kernel Command Line
In the RPi, backup the default cmdline.txt file it is required to disable diskless boot and revert to booting from local SD card.
# Backup original cmdline.txt
rpi:~# cp /boot/firmware/cmdline.txt /boot/firmware/cmdline.txt-localModify set the root, nfsroot and ip parameters in the cmdline.txt file.
| Parameter | Value |
|---|---|
| root | /dev/nfs |
| nfsroot | 10.13.1.231:/export/rpi,tcp,vers=3 |
| ip | ip=10.13.1.252::10.13.1.1:255.255.255.0:rpi:eth0:off |
Command line: root=/dev/nfs nfsroot=10.13.1.231:/export/rpi,tcp,vers=3 ip=10.13.1.252::10.13.1.1:255.255.255.0:rpi:eth0:off
rpi:~# cat /boot/firmware/cmdline.txt # Note: the below must be on a single line, not multiple lines
console=serial0,115200 console=tty1 root=/dev/nfs nfsroot=10.13.1.231:/export/rpi,tcp,vers=3 ip=10.13.1.252::10.13.1.1:255.255.255.0:rpi:eth0:off rootwait quiet splash plymouth.ignore-serial-consoles cfg80211.ieee80211_regdom=INStep 4: Copy /boot/firmware to NFS Server
In Raspberry, the /boot/firmware folder is on a separate vfat filesystem that can be read by the RPi boot loader. The rsync command in Step 2 will not have copied this folder to the NFS sever. Copy it manually.
# Copy /boot/firmware to NFS
cp -a /boot/firmware/* /mnt/boot/firmwareStep 5: Reboot and Verify
Reboot the RPi and run the mount command to verify that the rootfs is indeed on NFS.
root@rpi:~ # mount | grep nfs
10.13.1.231:/export/rpi on / type nfs (rw,noatime,vers=3,rsize=262144,wsize=262144,namlen=255,hard,nolock,proto=tcp,port=2049,timeo=600,retrans=3,sec=sys,local_lock=all,addr=10.13.1.231)Step 6: Performance Testing
RPi will provide between 80MB/s to 100MB/s of read/write throughput. This is almost saturating the wired 1Gb/s NIC.
root@rpi:/tmp# time dd if=/dev/urandom bs=1M count=1000 of=random
1000+0 records in
1000+0 records out
1048576000 bytes (1.0 GB, 1000 MiB) copied, 11.8835 s, 88.2 MB/s
real 0m11.894s
user 0m0.005s
sys 0m7.588s
root@rpi:/tmp# time dd if=/dev/zero bs=1M count=1000 of=zeroes
1000+0 records in
1000+0 records out
1048576000 bytes (1.0 GB, 1000 MiB) copied, 9.94405 s, 105 MB/s
real 0m9.951s
user 0m0.000s
sys 0m2.150s
# Clean up
root@rpi:/tmp# rm random zeroes
Step 7: Reverting to SD Card Boot
To revert NFS root and mount the SD Card again as the rootfs, just revert the /boot/firmware/cmdline.txt file back to the original one.
# Boot to local root fs on SD Card
root@rpi:~# cat /boot/firmware/cmdline.txt
console=serial0,115200 console=tty1 root=PARTUUID=1d3aa4bc-02 rootfstype=ext4 fsck.repair=yes rootwait quiet splash plymouth.ignore-serial-consoles cfg80211.ieee80211_regdom=INTips
- Save the SD Card root settings as
cmdline.txt-localand NFS root settings ascmdline.txt-nfs. Switching between local and NFS root is as simple as copying the required file ascmdline.txt. - To improve NFS read/write performance, ensure Proxmox server hosting
alpine-nfs-serverVM has zpool sync disabled (zfs set sync=disabled rpool) in the ZFS pool hosting the NFS server VM. See proxmox-best-practices,ZFS zpool Flags. - In
alpine-nfs-server, use a separate/exportvolume that will host the RPi NFS root. Do not combine it with the alpine root fs. This will provide easier data management.
Files
SD Card Boot cmdline
root@rpi:~# cat /boot/firmware/cmdline.txt-local
console=serial0,115200 console=tty1 root=PARTUUID=1d3aa4bc-02 rootfstype=ext4 fsck.repair=yes rootwait quiet splash plymouth.ignore-serial-consoles cfg80211.ieee80211_regdom=INNFS Boot cmdline
root@rpi:~# cat /boot/firmware/cmdline.txt-nfs
console=serial0,115200 console=tty1 root=/dev/nfs nfsroot=10.13.1.231:/export/rpi,tcp,vers=3 ip=10.13.1.252::10.13.1.1:255.255.255.0:rpi:eth0:off rootwait quiet splash plymouth.ignore-serial-consoles cfg80211.ieee80211_regdom=IN