Netgrimoire/ZNAS-NFS-Exports.md
2026-02-01 20:45:52 +00:00

6.8 KiB

title description published date tags editor dateCreated
ZFS-NFS-Exports Exporting NFS shares from ZFS datasets true 2026-02-01T20:45:40.210Z markdown 2026-02-01T20:45:40.210Z

NFS + ZFS Configuration Fix

Problems Identified

  1. Boot order issue: NFS was starting before ZFS mounted the datasets
  2. Autofs recursive loop: The server was NFS-mounting its own exports back to itself via autofs, creating conflicts
  3. Mountpoint mismatch: ZFS datasets were mounting to /srv/vault/* but NFS was trying to export from /export/* using bind mounts that didn't work properly
  4. Child dataset mountpoints: When the parent dataset mountpoint changed, child datasets still had old mountpoints and weren't visible

Complete Solution

Step 1: Stop and Disable Autofs on NFS Server

sudo systemctl stop autofs
sudo systemctl disable autofs

Note: Autofs should only run on NFS clients, not on the server itself.

Step 2: Set ZFS Datasets to Mount Directly to /export/*

Instead of using bind mounts, configure ZFS to mount directly to the export paths:

sudo zfs set mountpoint=/export vault
sudo zfs set mountpoint=/export/Data vault/Data
sudo zfs set mountpoint=/export/Green vault/Green
sudo zfs set mountpoint=/export/Docker vault/docker
sudo zfs set mountpoint=/export/Common vault/Common

Step 3: Configure Child Dataset Mountpoints

For any child datasets (subdirectories under parent datasets), set their mountpoints explicitly:

sudo zfs set mountpoint=/export/Data/media/books vault/Data/media_books
sudo zfs set mountpoint=/export/Data/media/comics vault/Data/media_comics

Step 4: Unmount and Remount Datasets

Unmount children first, then parents, then remount all:

# Unmount child datasets first
sudo zfs unmount vault/Data/media_books
sudo zfs unmount vault/Data/media_comics

# Unmount parent datasets
sudo zfs unmount vault/Data
sudo zfs unmount vault/Green
sudo zfs unmount vault/docker
sudo zfs unmount vault/Common

# Remount all
sudo zfs mount -a

Step 5: Configure NFS Exports

Edit /etc/exports:

sudo nano /etc/exports

Content:

# /etc/exports: the access control list for filesystems which may be exported
#               to NFS clients.  See exports(5).

# NFSv4 - pseudo filesystem root
/export *(ro,fsid=0,crossmnt,no_subtree_check)

# Shares beneath the NFSv4 root
/export/Common *(fsid=4,rw,no_subtree_check,insecure)
/export/Data *(fsid=5,rw,no_subtree_check,insecure,crossmnt,nohide)
/export/Docker *(fsid=29,rw,no_root_squash,sync,no_subtree_check,insecure)
/export/Green *(fsid=30,rw,no_root_squash,no_subtree_check,insecure)

Key options explained:

  • crossmnt: Allows NFS to cross filesystem mount boundaries
  • nohide: Makes child mounts visible through parent exports
  • no_subtree_check: Improves reliability and performance

Step 6: Configure NFS to Wait for ZFS at Boot

Create systemd override for NFS server:

sudo mkdir -p /etc/systemd/system/nfs-server.service.d/
sudo nano /etc/systemd/system/nfs-server.service.d/override.conf

Add this content:

[Unit]
After=zfs-import.target zfs-mount.service local-fs.target
Requires=zfs-import.target zfs-mount.service

This ensures NFS waits for ZFS to be fully mounted before starting.

Step 7: Reload and Restart Services

sudo systemctl daemon-reload
sudo exportfs -ra
sudo systemctl restart nfs-server

Step 8: Verify Configuration

On the server:

# Check ZFS mounts
zfs list -r vault

# Check what's actually mounted
mount | grep export

# Verify NFS exports
sudo exportfs -v

# Check content is visible
ls -la /export/Data/media/books/

On the client:

# Show available exports
showmount -e 192.168.5.10

# Mount NFS share
sudo mount -t nfs4 192.168.5.10:/ /mnt/znas

# Verify content
ls -la /mnt/znas/Data/media/books/

Adding New Datasets

When creating new datasets that need to be exported via NFS:

# Create dataset with correct mountpoint from the start
sudo zfs create -o mountpoint=/export/Data/new_folder vault/Data/new_folder

# The dataset will automatically mount and be visible via NFS
# due to the crossmnt and nohide options on the parent export

# Verify it's visible
ls -la /export/Data/new_folder/

No need to:

  • Modify /etc/exports (unless you need special permissions)
  • Create bind mounts in /etc/fstab
  • Restart NFS (it will see the new mount automatically)

Troubleshooting

Datasets not visible via NFS

# Verify dataset is mounted
zfs list | grep dataset_name

# Check NFS can read it
sudo -u nobody ls -la /export/path/to/dataset/

# Restart NFS
sudo exportfs -ra
sudo systemctl restart nfs-server

Client shows empty directories

# On client, clear NFS cache
sudo umount -f /mnt/znas
sudo mount -t nfs4 192.168.5.10:/ /mnt/znas

# Or mount with no caching to test
sudo mount -t nfs4 -o noac,lookupcache=none 192.168.5.10:/ /mnt/znas

After reboot, exports are empty

# Verify ZFS mounted before NFS started
systemctl status zfs-mount.service
systemctl status nfs-server.service

# Check the override is in place
systemctl cat nfs-server.service | grep -A5 Unit

Important Notes

  • Do not use bind mounts in /etc/fstab for ZFS datasets - let ZFS handle mounting directly
  • Keep autofs disabled on the NFS server to prevent recursive mount loops
  • Child datasets must have their mountpoints explicitly set to be visible
  • The crossmnt and nohide options are critical for NFSv4 to traverse ZFS dataset boundaries
  • Always set mountpoints when creating datasets to avoid having to fix them later

Configuration Files Reference

/etc/fstab

Should NOT contain bind mounts for /export/*. ZFS handles mounting directly.

Only keep system mounts:

# / was on /dev/nvme0n1p2 during curtin installation
/dev/disk/by-uuid/40c60952-0340-4a78-81f9-5b2193da26c6 / btrfs defaults 0 1
# /boot was on /dev/nvme0n1p3 during curtin installation
/dev/disk/by-uuid/4abb4efa-0b2b-4e4a-bcaf-78227db4628f /boot ext4 defaults 0 1
/dev/disk/by-uuid/d07437a0-3d0e-417a-a88e-438c603c2237 none swap sw 0 0
# /srv was on /dev/nvme0n1p5 during curtin installation
/dev/disk/by-uuid/c66e81ff-436e-4d6f-980b-6f4875ea7c8e /srv btrfs defaults 0 1

/etc/systemd/system/nfs-server.service.d/override.conf

[Unit]
After=zfs-import.target zfs-mount.service local-fs.target
Requires=zfs-import.target zfs-mount.service

/etc/exports

# NFSv4 - pseudo filesystem root
/export *(ro,fsid=0,crossmnt,no_subtree_check)

# Shares beneath the NFSv4 root
/export/Common *(fsid=4,rw,no_subtree_check,insecure)
/export/Data *(fsid=5,rw,no_subtree_check,insecure,crossmnt,nohide)
/export/Docker *(fsid=29,rw,no_root_squash,sync,no_subtree_check,insecure)
/export/Green *(fsid=30,rw,no_root_squash,no_subtree_check,insecure)