docs: create ZNAS-NFS-Exports
This commit is contained in:
parent
c801f69bf5
commit
a4de921c1c
1 changed files with 243 additions and 0 deletions
243
ZNAS-NFS-Exports.md
Normal file
243
ZNAS-NFS-Exports.md
Normal file
|
|
@ -0,0 +1,243 @@
|
|||
---
|
||||
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)
|
||||
```
|
||||
Loading…
Add table
Add a link
Reference in a new issue