| pick your distro, get ZFS on root
kldload — your platform, your way, free
Source

NFS and iSCSI Storage Sharing

Share ZFS datasets over the network via NFS or iSCSI. Works on all kldload distros.

NFS and iSCSI on ZFS are fundamentally different from NFS and iSCSI on ext4.

On ext4, an NFS share is a directory on a disk. If the disk has a bad sector, the NFS client gets corrupt data silently. There's no snapshot — if a user deletes a file on the share, it's gone. There's no per-share quota — one share can fill the disk and starve all other shares. There's no replication — DR means rsync jobs and hope.

On ZFS, each NFS share is a dataset. Checksummed on every read — corrupt data is detected and auto-repaired from mirrors. Snapshotted hourly — users can access .zfs/snapshot/ and recover their own files without calling IT. Quotaed independently — one share can't fill the pool. Replicable with zfs send — the entire share, with all its snapshots, replicates to DR atomically over WireGuard.

iSCSI on ZFS gets the same benefits: a zvol is a block device backed by ZFS. Snapshot a VM's iSCSI disk before a migration. Clone it for testing — instant, zero cost. The VM doesn't know it's on ZFS. It sees a block device. Everything underneath is checksummed, compressed, and replicable.


NFS — File-level sharing

Server setup

# CentOS/RHEL
kpkg install nfs-utils
systemctl enable --now nfs-server

# Debian
kpkg install nfs-kernel-server
systemctl enable --now nfs-kernel-server

Create a shared dataset

# Create a ZFS dataset for sharing
zfs create -o mountpoint=/srv/nfs-share \
           -o compression=lz4 \
           rpool/srv/nfs-share

# Set ZFS NFS sharing (alternative to /etc/exports)
zfs set sharenfs="rw=@10.78.0.0/16,no_root_squash" rpool/srv/nfs-share

Or use traditional /etc/exports:

cat >> /etc/exports << 'EOF'
/srv/nfs-share  10.78.0.0/16(rw,no_root_squash,no_subtree_check,sync)
EOF

exportfs -ra

Open the firewall

# CentOS/RHEL
firewall-cmd --permanent --add-service=nfs
firewall-cmd --permanent --add-service=rpc-bind
firewall-cmd --permanent --add-service=mountd
firewall-cmd --reload

# Debian
nft add rule inet filter input tcp dport 2049 accept

Client setup

# CentOS/RHEL
kpkg install nfs-utils

# Debian
kpkg install nfs-common
# Mount
mkdir -p /mnt/shared
mount -t nfs 10.78.7.1:/srv/nfs-share /mnt/shared

# Verify
df -h /mnt/shared

# Persist in fstab
echo "10.78.7.1:/srv/nfs-share /mnt/shared nfs defaults,_netdev 0 0" >> /etc/fstab

Multiple exports with different permissions

# Read-write share for the team
zfs create rpool/srv/project-data
echo "/srv/project-data 10.78.0.0/16(rw,sync,no_subtree_check)" >> /etc/exports

# Read-only ISO library
zfs create rpool/srv/iso-library
echo "/srv/iso-library 10.78.0.0/16(ro,sync,no_subtree_check)" >> /etc/exports

# Home directories with root squash
echo "/home 10.78.0.0/16(rw,sync,no_subtree_check,root_squash)" >> /etc/exports

exportfs -ra

iSCSI — Block-level sharing

iSCSI presents a ZFS volume as a raw block device over the network. Useful for VMs, databases, or any workload that needs a dedicated block device.

Server setup (target)

# CentOS/RHEL
kpkg install targetcli

# Debian
kpkg install targetcli-fb

Create a ZFS volume (zvol)

# Create a 100GB zvol (thin-provisioned by default on ZFS)
zfs create -V 100G rpool/iscsi/vm-disk-1

# The block device appears at:
ls -l /dev/zvol/rpool/iscsi/vm-disk-1
# → /dev/zd0 (or similar)

Configure the iSCSI target

targetcli

# Inside targetcli:
/> cd /backstores/block
/backstores/block> create vm-disk-1 /dev/zvol/rpool/iscsi/vm-disk-1

/> cd /iscsi
/iscsi> create iqn.2026-03.com.kldload:storage.vm-disk-1

/> cd /iscsi/iqn.2026-03.com.kldload:storage.vm-disk-1/tpg1/luns
/iscsi/.../luns> create /backstores/block/vm-disk-1

/> cd /iscsi/iqn.2026-03.com.kldload:storage.vm-disk-1/tpg1/acls
/iscsi/.../acls> create iqn.2026-03.com.kldload:client1

/> saveconfig
/> exit
systemctl enable --now target

Open the firewall

# CentOS/RHEL
firewall-cmd --permanent --add-port=3260/tcp
firewall-cmd --reload

# Debian
nft add rule inet filter input tcp dport 3260 accept

Client setup (initiator)

# CentOS/RHEL
kpkg install iscsi-initiator-utils

# Debian
kpkg install open-iscsi

Set the client IQN:

echo "InitiatorName=iqn.2026-03.com.kldload:client1" > /etc/iscsi/initiatorname.iscsi
systemctl restart iscsid

Discover and connect

# Discover targets
iscsiadm -m discovery -t sendtargets -p 10.78.7.1:3260

# Login
iscsiadm -m node \
  -T iqn.2026-03.com.kldload:storage.vm-disk-1 \
  -p 10.78.7.1:3260 \
  --login

# Find the new block device
lsblk
# sdb   8:16   0  100G  0 disk

# Format and mount (or use as a raw device for a VM)
mkfs.ext4 /dev/sdb
mkdir -p /mnt/iscsi-vol
mount /dev/sdb /mnt/iscsi-vol

Auto-connect at boot

iscsiadm -m node \
  -T iqn.2026-03.com.kldload:storage.vm-disk-1 \
  -p 10.78.7.1:3260 \
  --op update -n node.startup -v automatic

systemctl enable iscsid

The WireGuard angle. All the NFS/iSCSI examples above use 10.78.0.0/16 — that's a WireGuard mesh subnet. On a kldload network, NFS and iSCSI traffic flows over encrypted WireGuard tunnels by default. Your file shares and block devices are encrypted in transit at the kernel level without any NFS/iSCSI-specific encryption config. No Kerberos. No stunnel. No IPsec. The tunnel is already there. The storage traffic rides on it.

Per-share datasets as service boundaries. Don't put all your NFS shares on one dataset. Create a dataset per share: rpool/srv/project-data, rpool/srv/iso-library, rpool/srv/home-dirs. Each one gets its own compression (zstd for documents, off for ISOs), its own quota (project can't eat the ISO library's space), its own snapshot schedule (hourly for project data, daily for ISOs), and its own replication target. The shares are independent storage domains. The NFS layer just exposes them.

ZFS advantages for storage sharing

Snapshots of shared data

# Snapshot the NFS share
zfs snapshot rpool/srv/nfs-share@hourly-$(date +%H)

# Expose snapshots to NFS clients (ZFS .zfs directory)
zfs set snapdir=visible rpool/srv/nfs-share

# Clients can access snapshots at:
# /mnt/shared/.zfs/snapshot/hourly-14/

Snapshot an iSCSI zvol

zfs snapshot rpool/iscsi/vm-disk-1@before-migration

# Clone for testing
zfs clone rpool/iscsi/vm-disk-1@before-migration rpool/iscsi/vm-disk-1-test
# New block device at /dev/zvol/rpool/iscsi/vm-disk-1-test

Compression

NFS data is compressed transparently by ZFS on the server side. Clients see uncompressed data:

zfs get compressratio rpool/srv/nfs-share
# NAME                  PROPERTY       VALUE
# rpool/srv/nfs-share   compressratio  2.31x
Users can browse /mnt/shared/.zfs/snapshot/ and recover their own deleted files. No helpdesk ticket. No restore from backup. They navigate to the snapshot, copy the file back, and move on. This single feature eliminates the most common storage support request in any organization. Set snapdir=visible on every NFS share and tell your users about it. Watch your support tickets drop.

Quotas per share

# Limit an NFS share to 500GB
zfs set quota=500G rpool/srv/nfs-share

# Guarantee 200GB (reservation)
zfs set reservation=200G rpool/srv/nfs-share

Troubleshooting

# NFS — show current exports
exportfs -v
showmount -e localhost

# NFS — client can't mount
rpcinfo -p <server-ip>    # check RPC services running
mount -v -t nfs <server>:/path /mnt   # verbose mount

# iSCSI — show active sessions
iscsiadm -m session

# iSCSI — disconnect
iscsiadm -m node -T <iqn> -p <ip>:3260 --logout

# iSCSI — show target config
targetcli ls