TrueNAS CSI Driver

September 4, 2026 · View on GitHub

A Container Storage Interface (CSI) driver for TrueNAS 25.10.0+, enabling dynamic provisioning of persistent volumes in Kubernetes using TrueNAS storage.

Features

  • NFS volumes - ReadWriteMany (RWX) access mode for shared storage
  • iSCSI volumes - Block storage with ReadWriteOnce (RWO) and ReadWriteMany (RWX) access modes (RWX requires cluster filesystem like GFS2/OCFS2)
  • NVMe-oF/TCP volumes - Block storage over NVMe over Fabrics (TCP) with optional DH-CHAP authentication
  • Dynamic provisioning - Automatic volume creation and deletion
  • Volume expansion - Online resize of volumes
  • Snapshots and clones - CSI snapshot support for backup and cloning
  • CHAP authentication - Secure iSCSI connections
  • ZFS compression - LZ4, ZSTD, GZIP, and other algorithms
  • ZFS encryption - Dataset-level encryption with key management
  • Automatic snapshot scheduling - Periodic snapshots via StorageClass
  • TrueNAS Websocket API - Uses the modern TrueNAS Websocket API
  • Prometheus metrics - Optional /metrics endpoint for CSI operations and TrueNAS API health (docs)

Requirements

TrueNAS

  • TrueNAS SCALE 25.10.0+
  • API access enabled
  • At least one ZFS pool configured

Kubernetes

Node Requirements

  • NFS volumes: No additional requirements
  • iSCSI volumes: open-iscsi package installed on worker nodes
  • NVMe-oF volumes: nvme_tcp/nvme_fabrics kernel modules available on worker nodes (the node DaemonSet loads them); requires TrueNAS SCALE 25.10+ with the NVMe-oF target service enabled

Quick Start

  1. Create an API key in TrueNAS

    • Log into TrueNAS web UI
    • Navigate to your profile → API Keys
    • Create a new API key and copy it
  2. Configure the driver

    # Edit the deployment manifest
    vi deploy/truenas-csi-driver.yaml
    

    Update the ConfigMap with your TrueNAS connection details and the Secret with your API key.

  3. Deploy the driver

    kubectl apply -f deploy/truenas-csi-driver.yaml
    
  4. Create a StorageClass and PVC

    kubectl apply -f examples/storageclass-nfs.yaml
    kubectl apply -f examples/pvc-nfs.yaml
    

Installation

Prerequisites

Install the snapshot controller (required for snapshot support):

kubectl apply -f https://raw.githubusercontent.com/kubernetes-csi/external-snapshotter/master/client/config/crd/snapshot.storage.k8s.io_volumesnapshotclasses.yaml
kubectl apply -f https://raw.githubusercontent.com/kubernetes-csi/external-snapshotter/master/client/config/crd/snapshot.storage.k8s.io_volumesnapshotcontents.yaml
kubectl apply -f https://raw.githubusercontent.com/kubernetes-csi/external-snapshotter/master/client/config/crd/snapshot.storage.k8s.io_volumesnapshots.yaml
kubectl apply -f https://raw.githubusercontent.com/kubernetes-csi/external-snapshotter/master/deploy/kubernetes/snapshot-controller/rbac-snapshot-controller.yaml
kubectl apply -f https://raw.githubusercontent.com/kubernetes-csi/external-snapshotter/master/deploy/kubernetes/snapshot-controller/setup-snapshot-controller.yaml

Deploy the Driver

Either install path works; they deploy the same objects.

Helm (chart reference):

helm repo add truenas-csi https://raw.githubusercontent.com/truenas/truenas-csi/master/charts
helm install truenas-csi truenas-csi/truenas-csi \
  --namespace truenas-csi --create-namespace \
  --set truenas.url=wss://YOUR-TRUENAS-IP \
  --set truenas.apiKey=YOUR-API-KEY \
  --set truenas.defaultPool=tank \
  --set truenas.insecureSkipTLS=true

Manifest:

  1. Edit deploy/truenas-csi-driver.yaml with your configuration
  2. Apply the manifest:
    kubectl apply -f deploy/truenas-csi-driver.yaml
    

Verify Installation

# Check driver pods are running
kubectl get pods -n truenas-csi

# Verify CSI driver is registered
kubectl get csidrivers

Non-Standard Kubelet Paths

The default deployment manifest uses /var/lib/kubelet as the kubelet root directory. Some Kubernetes distributions use a different path. If your distribution uses a non-standard path, you must update the following in deploy/truenas-csi-driver.yaml before deploying:

  1. All hostPath values containing /var/lib/kubelet
  2. The DRIVER_REG_SOCK_PATH environment variable
  3. The --kubelet-registration-path argument
  4. The mountPath for the kubelet-dir volume mount on the csi-node container
DistributionKubelet Path
Standard Kubernetes/var/lib/kubelet (default)
K3s/var/lib/kubelet
MicroK8s/var/snap/microk8s/common/var/lib/kubelet

Important: The kubelet-dir mountPath must match the hostPath. If they differ, NFS mounts will succeed inside the CSI container but will not propagate to kubelet, causing pods to see local storage instead of NFS.

MicroK8s Mount Propagation

MicroK8s runs inside a snap with its own mount namespace. For CSI mount propagation to work, the host root filesystem must have shared propagation before MicroK8s starts:

sudo mount --make-rshared /
microk8s start

To make this persistent across reboots, create a systemd unit:

sudo tee /etc/systemd/system/microk8s-mount-propagation.service <<EOF
[Unit]
Description=Ensure shared mount propagation for MicroK8s
Before=snap.microk8s.daemon-containerd.service

[Service]
Type=oneshot
ExecStart=/bin/mount --make-rshared /
RemainAfterExit=yes

[Install]
WantedBy=multi-user.target
EOF

sudo systemctl enable microk8s-mount-propagation

Configuration

The settings below are the ConfigMap keys used by deploy/truenas-csi-driver.yaml. With Helm they are set through values instead; see the chart reference for the mapping.

Driver Configuration (ConfigMap)

SettingDescriptionExample
truenasURLWebSocket URL to TrueNAS APIwss://10.0.0.100/api/current
truenasInsecureSkip TLS verificationtrue (for self-signed certs)
defaultPoolDefault ZFS pool for volumestank
nfsServerNFS server address10.0.0.100
iscsiPortaliSCSI portal address10.0.0.100:3260
nvmeofPortalNVMe-oF portal address (optional; auto-derived)10.0.0.100:4420
iscsiIQNBaseBase IQN for iSCSI targetsiqn.2024-01.com.example
metricsAddrPrometheus metrics address for the controller (optional; disabled when absent):8080
nodeMetricsAddrPrometheus metrics address for the node plugin, which uses hostNetwork (optional):8080

StorageClass Parameters

General Parameters

ParameterDescriptionValues
protocolStorage protocolnfs, iscsi, nvmeof
poolZFS pool (overrides default)pool name
datasetPathParent path for volume datasets, relative to the pool (no pool prefix, no leading/trailing /, no ..). If unset, volumes are created at the pool root (pool/<pvc-name>); e.g. k8s/iscsipool/k8s/iscsi/<pvc-name>relative path
compressionZFS compression algorithmOFF, LZ4, GZIP[-1|-9], ZSTD[-1..-9], ZLE, LZJB
syncZFS sync modeSTANDARD, ALWAYS, DISABLED
sparseThin-provision the ZVOL (iSCSI/NVMe-oF); default falsetrue, false

Delete-time behavior (optional): forceDelete (true/false) forces removal of busy resources; deleteExtentsWithTarget (true/false, default true) removes the iSCSI extent along with its target.

NFS Parameters

ParameterDescriptionExample
nfs.hostsAllowed hosts10.0.0.0/8,192.168.1.0/24
nfs.networksAllowed networks10.0.0.0/8
nfs.mountOptionsClient mount optionshard,nfsvers=4.1
nfs.mapAllUserNFS user mapping (default: root)postgres
nfs.mapAllGroupNFS group mapping (default: wheel)postgres
nfs.rootSquashSquash all access to the mapped user (default: true). Set false for no_root_squash so a pod fsGroup can chown the volume root — required for ownership-sensitive non-root workloads (e.g. PostgreSQL/CNPG)false

By default an NFS share squashes all client access to a single user (mapall, root:wheel). Ownership-sensitive workloads that run as a non-root user (such as PostgreSQL/CloudNativePG) need to own their data directory, which mapall cannot provide. Set nfs.rootSquash: "false" to switch the share to no_root_squash: incoming root is preserved so the kubelet (via the driver's fsGroupPolicy: File) can chown the volume root to the pod's fsGroup, and non-root UIDs are no longer squashed. Requires the workload to set a pod securityContext.fsGroup. See examples/storageclass-nfs-fsgroup.yaml.

iSCSI Parameters

ParameterDescriptionValues
volblocksizeZVOL block size512, 1K, 2K, 4K, 8K, 16K, 32K, 64K, 128K
iscsi.blocksizeiSCSI logical block size512, 1024, 2048, 4096
iscsi.iqn-baseOverride the IQN base (auto-derived from the appliance's iscsi.global.basename by default)IQN string
iscsi.initiatorsAllowed initiator IQNscomma-separated
iscsi.chapUserCHAP usernamestring
iscsi.chapSecretCHAP password (12-16 chars)string
iscsi.chapPeerUserMutual CHAP peer userstring
iscsi.chapPeerSecretMutual CHAP peer passwordstring
iscsi.multipathEnabledEnable multipath for the session (node-side); default falsetrue, false
iscsi.persistentSessionsKeep the iSCSI session persistent (node-side); default falsetrue, false

IPv4 only: iSCSI portals must be IPv4. The pinned csi-lib-iscsi mis-parses IPv6 portal addresses, so iSCSI staging fails on IPv6-only clusters — use NFS there. The driver fails fast with a clear error if an IPv6 iSCSI portal is configured.

NVMe-oF Parameters

NVMe-oF also uses the volblocksize parameter above. DH-CHAP authentication is optional.

ParameterDescriptionValues
nvmeof.hostNQNAuthorized host NQN (required for DH-CHAP)nqn.2014-08.org.nvmexpress:uuid:...
nvmeof.dhchapKeyDH-CHAP host keyDHHC-1:00:...
nvmeof.dhchapCtrlKeyMutual DH-CHAP controller keyDHHC-1:00:...
nvmeof.dhchapHashDH-CHAP hash (default SHA-256)SHA-256, SHA-384, SHA-512
nvmeof.dhchapDHGroupDH group2048-BIT, 3072-BIT, 4096-BIT, 6144-BIT, 8192-BIT

Snapshot Task Parameters

ParameterDescriptionValues
snapshot.scheduleCron schedule (5 fields)0 0 * * *
snapshot.retentionRetention period1-365
snapshot.retentionUnitRetention unitHOUR, DAY, WEEK, MONTH, YEAR
snapshot.namingNaming schemaauto-%Y-%m-%d_%H-%M
snapshot.recursiveInclude child datasetstrue, false

Encryption Parameters

ParameterDescriptionValues
encryptionEnable encryptiontrue, false
encryption.algorithmEncryption algorithmAES-256-GCM, AES-128-CCM
encryption.passphrasePassphrase (min 8 chars)string
encryption.keyHex-encoded key (64 chars)string
encryption.generateKeyAuto-generate keytrue, false

Examples

See the examples/ folder for sample configurations:

  • storageclass-nfs.yaml - Basic NFS StorageClass
  • storageclass-nfs-compressed.yaml - NFS with ZSTD compression
  • storageclass-nfs-fsgroup.yaml - NFS for ownership-sensitive non-root workloads (no_root_squash + pod fsGroup)
  • storageclass-iscsi.yaml - Basic iSCSI StorageClass
  • storageclass-iscsi-chap.yaml - iSCSI with CHAP authentication
  • storageclass-nvmeof.yaml - Basic NVMe-oF/TCP StorageClass
  • storageclass-nvmeof-dhchap.yaml - NVMe-oF with DH-CHAP authentication
  • storageclass-encrypted.yaml - Encrypted storage
  • pvc-nfs.yaml / pvc-iscsi.yaml / pvc-nvmeof.yaml - PVC examples
  • pod-with-pvc.yaml - Pod using a PVC
  • volumesnapshotclass.yaml / volumesnapshot.yaml - Snapshot examples

Building

Build the binary

make build

Build container images

# Build Alpine-based image (standard Kubernetes)
make docker-build

# Build UBI-based image (Red Hat OpenShift certification)
make build-ubi

Push to quay.io

# Login to quay.io
docker login quay.io

# Push UBI image to quay.io/truenas_solutions
make push-ubi

# Push all images (driver, operator, bundle)
make push-all

Run tests

make test

Container Images

ImageDescription
ghcr.io/truenas/truenas-csiCSI driver (Alpine-based, for standard Kubernetes)
quay.io/truenas_solutions/truenas-csiCSI driver (UBI-based, for Red Hat OpenShift)
quay.io/truenas_solutions/truenas-csi-operatorKubernetes operator
quay.io/truenas_solutions/truenas-csi-operator-bundleOLM bundle for OperatorHub

Running the Demo

For an interactive demonstration of all driver features using a local Kind cluster, see docs/demo.md.

OpenShift

The TrueNAS CSI Driver supports Red Hat OpenShift 4.20+ and is designed for OperatorHub distribution.

Quick Start (OpenShift)

  1. Install via OperatorHub

    • Navigate to Operators > OperatorHub
    • Search for "TrueNAS CSI"
    • Click Install
  2. Create credentials secret

    apiVersion: v1
    kind: Secret
    metadata:
      name: truenas-api-credentials
      namespace: truenas-csi
    stringData:
      api-key: "YOUR-API-KEY"
    
  3. Create TrueNASCSI resource

    apiVersion: csi.truenas.io/v1alpha1
    kind: TrueNASCSI
    metadata:
      name: truenas
    spec:
      truenasURL: "wss://your-truenas-ip/api/current"
      credentialsSecret: "truenas-api-credentials"
      defaultPool: "tank"
      nfsServer: "your-truenas-ip"
    

OpenShift Documentation

Demo Scripts

Interactive demo scripts are provided to test the CSI driver:

Standard Kubernetes (Kind)

# Set TrueNAS connection details in deploy/truenas-csi-driver.yaml, then:
./demo-simple.sh

OpenShift (CRC/OpenShift Local)

# Set environment variables
export TRUENAS_IP=192.168.1.100
export TRUENAS_API_KEY=your-api-key
export TRUENAS_POOL=tank

# Run the demo
./demo-openshift.sh

Both demos provide interactive menus to test NFS/iSCSI provisioning, volume expansion, snapshots, and cloning.

Contributing

License

GNU General Public License 3.0