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

243 lines
No EOL
6.8 KiB
Markdown

---
title: ZFS-NFS-Exports
description: Exporting NFS shares from ZFS datasets
published: true
date: 2026-02-01T20:45:40.210Z
tags:
editor: markdown
dateCreated: 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
```bash
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:
```bash
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:
```bash
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:
```bash
# 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`:
```bash
sudo nano /etc/exports
```
Content:
```bash
# /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:
```bash
sudo mkdir -p /etc/systemd/system/nfs-server.service.d/
sudo nano /etc/systemd/system/nfs-server.service.d/override.conf
```
Add this content:
```ini
[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
```bash
sudo systemctl daemon-reload
sudo exportfs -ra
sudo systemctl restart nfs-server
```
### Step 8: Verify Configuration
On the server:
```bash
# 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:
```bash
# 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:
```bash
# 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
```bash
# 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
```bash
# 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
```bash
# 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:
```bash
# / 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
```ini
[Unit]
After=zfs-import.target zfs-mount.service local-fs.target
Requires=zfs-import.target zfs-mount.service
```
### /etc/exports
```bash
# 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)
```