How to Build an NFS Server on Raspberry Pi

I’d like to start setting up a PXE boot environment for Raspberry Pis on my network. Essentially, I should be able to boot a Raspberry just by plugging in the Ethernet cable and the power cable, and if everything is set up properly, the thing will boot with NO LOCAL MEDIA–no SD card, no USB drives, or no NVMe HAT!

The first part of this will be setting up a server that will provide NFS and TFTP. The NFS will offer the /root partition to each of the Raspberries that boot from it. The TFTP will provide the /boot partition files that make up the bootstrap files that get the Raspberry running enough to load the files in the /root partition and start running what looks like LINUX!

The magic is setting up DHCP to tell the Raspberry, “Here’s your IP address, and oh, by the way, here’s where to find your boot files on the network (since you can’t find your SD card to boot from!”

I’ll start by setting up the file server. Let’s call it “boots”. I’m going to store my files on a 60GB USB drive. Using lsblk, I can confirm that the Raspberry sees it:

root@boots:~# lsblk
NAME   MAJ:MIN RM  SIZE RO TYPE MOUNTPOINT
sda      8:0    1 28.7G  0 disk
├─sda1   8:1    1  256M  0 part /boot
└─sda2   8:2    1 28.4G  0 part /
sdb      8:16   1   60M  0 disk

Let’s create a partition on it:

root@boots:~# fdisk /dev/sdb

Welcome to fdisk (util-linux 2.36.1).
Changes will remain in memory only, until you decide to write them.
Be careful before using the write command.


Command (m for help): p
Disk /dev/sdb: 60 MiB, 62914560 bytes, 122880 sectors
Disk model: Flash-Disk
Units: sectors of 1 * 512 = 512 bytes
Sector size (logical/physical): 512 bytes / 512 bytes
I/O size (minimum/optimal): 512 bytes / 512 bytes
Disklabel type: gpt
Disk identifier: 29EC7820-7E2F-4FD1-BE65-A55AE0A971EE

Command (m for help): n
Partition number (1-128, default 1): 1
First sector (34-122846, default 2048):  <<enter>>
Last sector, +/-sectors or +/-size{K,M,G,T,P} (2048-122846, default 122846): <<enter>>

Created a new partition 1 of type 'Linux filesystem' and of size 59 MiB.

Command (m for help): p
Disk /dev/sdb: 60 MiB, 62914560 bytes, 122880 sectors
Disk model: Flash-Disk
Units: sectors of 1 * 512 = 512 bytes
Sector size (logical/physical): 512 bytes / 512 bytes
I/O size (minimum/optimal): 512 bytes / 512 bytes
Disklabel type: gpt
Disk identifier: 29EC7820-7E2F-4FD1-BE65-A55AE0A971EE

Device     Start    End Sectors Size Type
/dev/sdb1   2048 122846  120799  59M Linux filesystem

Command (m for help): w
The partition table has been altered.
Calling ioctl() to re-read partition table.
Syncing disks.

root@boots:~# 

Once the partition is in place, we can format it:

root@boots:~# mkfs /dev/sdb1

mke2fs 1.46.2 (28-Feb-2021)

Creating filesystem with 60396 1k blocks and 15104 inodes

Filesystem UUID: ae5309bc-7958-4cda-8d3e-7b701f0080d8

Superblock backups stored on blocks:

        8193, 24577, 40961, 57345



Allocating group tables: done

Writing inode tables: done

Writing superblocks and filesystem accounting information: done

root@boots:~#

Next step, create a mount point and mount the new drive.

root@boots:~# mkdir /data1

root@boots:~# mount /dev/sdb1 /data1

root@boots:~# df -h
Filesystem            Size  Used Avail Use% Mounted on
/dev/root              28G  4.0G   23G  15% /
devtmpfs              1.7G     0  1.7G   0% /dev
tmpfs                 1.9G   16K  1.9G   1% /dev/shm
tmpfs                 759M  868K  758M   1% /run
tmpfs                 5.0M  4.0K  5.0M   1% /run/lock
/dev/sda1             253M   30M  223M  12% /boot
rock:/data/clusterfs   19T   13T  5.3T  72% /work
tmpfs                 380M   16K  380M   1% /run/user/0
/dev/sdb1              56M   14K   54M   1% /data1
root@boots:~#

OK! Look good. We’ve got a 54M /data1 partition to share our PXE files. (Don’t forget to add that to /etc/fstab so it’s still there after a reboot!)

Next, we’ll need to install some software:

apt install -y nfs-kernel-server

Let’s keep the configuration simple for the time being. If we decide to keep this, we can tighten up security later. I just like to make sure everything works first. Add a line to /etc/exports:

/data1 *(rw,sync,no_subtree_check,no_root_squash)

This line is very powerful! NO SPACE between the * and the (. Of all the troubleshooting to get this to work, NFS gave me the most issues!

Enable and start the NFS server

With the packages installed and the directory added to your /etc/fstab file, you can now start the NFS server.

sudo systemctl enable nfs-server.service
sudo systemctl start nfs-server.service

Let’s jump on another Raspberry to make sure this is working:

root@node01:~# mount boots:/data1 /mnt
root@node01:~# df -h
Filesystem            Size  Used Avail Use% Mounted on
/dev/root              28G  4.9G   22G  19% /
devtmpfs              326M     0  326M   0% /dev
tmpfs                 455M  140K  455M   1% /dev/shm
tmpfs                 182M  820K  182M   1% /run
tmpfs                 5.0M  4.0K  5.0M   1% /run/lock
/dev/sda1             253M   30M  223M  12% /boot
rock:/data/clusterfs   19T   13T  5.3T  72% /work
tmpfs                  91M   24K   91M   1% /run/user/1000
tmpfs                  91M   16K   91M   1% /run/user/0
boots:/data1           28G  4.0G   23G  15% /mnt

Yep, it mounts! The Size is a little suspicious. Let’s move along for now, but I’m keeping my eye on that volume size!

Next, a tftp server for the boot files!

Leave a Reply

Your email address will not be published.

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