Configure NFS volumes
Serve shared ZFS datasets over NFSv4.2 without installing NFS userspace packages on Kubernetes nodes.
On this page
NFS volumes use a ZFS dataset as the backend and NFSv4.2 as the network protocol. The storage node serves the dataset on TCP port 2049; the node image carries the NFS mount helper. Kubernetes nodes need kernel NFS support, but they do not need nfs-common, nfs-utils, mount.nfs, or another host-side NFS package.
NFS is a single-storage-node export. ReadWriteMany permits concurrent mounts, but it does not replicate the dataset or make it available when the storage node is down.
Before you start
Prepare all of the following:
- A ZFS pool and a persistent, NFS-exportable backing filesystem for the agent’s dataset pseudoroot.
- NFS server support (
nfsd) on the storage node. - NFS client support (
nfsandnfsv4, or built-in kernel support) on every worker that can mount the volume. - The
agent.backendsentry for the ZFS pool in Helm values. - A
PillarAgentthat points at the storage node.
The production pseudoroot is /var/lib/pillar-csi/agent/datasets. It must persist across agent and node restarts and must support stable NFS export filehandles. Container overlay/rootfs is not supported for this path. See Prerequisites.
If the kernel modules are loaded after pillar-node starts, restart the pillar-node Pod. The startup check is intentional: a node that cannot mount NFS does not advertise NFS client capability.
Define the dataset store
Set the ZFS volume type to dataset:
apiVersion: pillar-csi.bhyoo.com/v1alpha1
kind: PillarStore
metadata:
name: storage-1-datasets
spec:
agentRef: storage-1
backend:
zfs:
pool: tank
parentDataset: k8s
volumeType: datasetThe parentDataset must match the agent’s agent.backends[].zfs.parentDataset. A zfs-zvol store cannot be paired with NFS.
Define the NFS protocol
NFS settings belong to the PillarProtocol:
apiVersion: pillar-csi.bhyoo.com/v1alpha1
kind: PillarProtocol
metadata:
name: nfs-default
spec:
protocol:
nfs:
version: "4.2"
port: 2049
acl: true
squash: rootThe version and port are fixed by the driver. acl: true limits access to the InternalIP addresses of nodes published to the volume. squash: root is the default and prevents remote root from becoming root on the dataset.
Create a storage class binding
Join the dataset store and NFS protocol with a PillarStorageClass:
apiVersion: pillar-csi.bhyoo.com/v1alpha1
kind: PillarStorageClass
metadata:
name: shared-datasets
spec:
storeRef: storage-1-datasets
protocolRef: nfs-default
filesystem:
mountOptions:
- noatimeReadWriteMany is supported for NFS. ReadOnlyMany creates a volume-wide read-only export. A volume cannot mix writable and read-only-many capabilities.
Per-volume settings
NFS has no per-volume protocol tuning. This document is accepted for the common override shape but does not change NFS behavior:
pillar-csi.bhyoo.com/protocol: |
nfs: {}Do not put version, port, acl, or squash in that document. Those fields are structural and are rejected. Set them on the referenced PillarProtocol instead.
For one volume, use the filesystem annotation only for mount options:
metadata:
annotations:
pillar-csi.bhyoo.com/filesystem: |
mountOptions:
- noatimefsType may be omitted or set to nfs. mkfsOptions, enabled periodicTrim, block volume mode, localAttach, and contradictory NFS mount flags are rejected. The controller adds the required NFSv4.2, TCP, hard-mount and port settings.
Access and filesystem ownership
NFS publishes access for each node through ControllerPublishVolume. With acl: true, revoked or unpublished node IPs cannot access the dataset. The export still comes from the one storage node that owns the ZFS pool.
When a workload needs root or fsGroup initialization to write the dataset, choose squash: none explicitly. This changes authorization; it does not encrypt NFS traffic. NFS RPC TLS is not offered.
Expand and recover
Edit the PVC’s requested capacity as usual. The agent increases the server-side ZFS dataset quota; the mounted client sees the new capacity without a node-side filesystem resize.
After an agent restart or storage-node reboot, the controller replays the durable export state. The agent restores the managed root and child dataset exports, ACL membership, read-only state, squash policy and NFS version. Foreign NFS exports are not stopped or reconfigured.
If the volume does not mount, check:
kubectl describe pvc <claim>
kubectl describe pillaragent <agent>
kubectl describe pillarstore <store>Then verify the worker’s NFS client support and the storage node’s persistent pseudoroot. See Troubleshooting for the common NFS errors.