--- 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) ```