A Container Storage Interface (CSI) driver for TrueNAS 25.10.0+, enabling dynamic provisioning of persistent volumes in Kubernetes using TrueNAS storage.
- 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 (docs)
- 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
/metricsendpoint for CSI operations and TrueNAS API health (docs) - Private CA trust - Verify a TrueNAS certificate issued by your own CA (docs)
- Outbound proxy - Reach the TrueNAS API through an HTTP proxy (docs)
- TrueNAS SCALE 25.10.0+
- API access enabled
- At least one ZFS pool configured
- Kubernetes 1.26+
- For snapshots: snapshot-controller installed
- NFS volumes: No additional requirements
- iSCSI volumes:
open-iscsipackage installed on worker nodes - NVMe-oF volumes:
nvme_tcp/nvme_fabricskernel modules available on worker nodes (the node DaemonSet loads them); requires TrueNAS SCALE 25.10+ with the NVMe-oF target service enabled
-
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
-
Configure the driver
# Edit the deployment manifest vi deploy/truenas-csi-driver.yamlUpdate the ConfigMap with your TrueNAS connection details and the Secret with your API key.
-
Deploy the driver
kubectl apply -f deploy/truenas-csi-driver.yaml
-
Create a StorageClass and PVC
kubectl apply -f examples/storageclass-nfs.yaml kubectl apply -f examples/pvc-nfs.yaml
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.yamlEither 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=trueManifest:
- Edit
deploy/truenas-csi-driver.yamlwith your configuration - Apply the manifest:
kubectl apply -f deploy/truenas-csi-driver.yaml
# Check driver pods are running
kubectl get pods -n truenas-csi
# Verify CSI driver is registered
kubectl get csidriversThe 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:
- All
hostPathvalues containing/var/lib/kubelet - The
DRIVER_REG_SOCK_PATHenvironment variable - The
--kubelet-registration-pathargument - The
mountPathfor thekubelet-dirvolume mount on thecsi-nodecontainer
| Distribution | Kubelet Path |
|---|---|
| Standard Kubernetes | /var/lib/kubelet (default) |
| K3s | /var/lib/kubelet |
| MicroK8s | /var/snap/microk8s/common/var/lib/kubelet |
Important: The
kubelet-dirmountPathmust match thehostPath. 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 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 startTo 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-propagationThe 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.
| Setting | Description | Example |
|---|---|---|
truenasURL |
WebSocket URL to TrueNAS API | wss://10.0.0.100/api/current |
truenasInsecure |
Skip TLS verification | true (for self-signed certs) |
defaultPool |
Default ZFS pool for volumes | tank |
nfsServer |
NFS server address | 10.0.0.100 |
iscsiPortal |
iSCSI portal address | 10.0.0.100:3260 |
nvmeofPortal |
NVMe-oF portal address (optional; auto-derived) | 10.0.0.100:4420 |
iscsiIQNBase |
Base IQN for iSCSI targets | iqn.2024-01.com.example |
metricsAddr |
Prometheus metrics address for the controller (optional; disabled when absent) | :8080 |
nodeMetricsAddr |
Prometheus metrics address for the node plugin, which uses hostNetwork (optional) | :8080 |
httpsProxy, httpProxy, noProxy |
Outbound proxy for the TrueNAS API (optional); see Outbound Proxy | http://proxy.example:3128 |
The pods read these settings, and the API key from the Secret, only when they start. After editing either one, restart both workloads to apply the change:
kubectl -n truenas-csi rollout restart deployment/truenas-csi-controller
kubectl -n truenas-csi rollout restart daemonset/truenas-csi-nodeThe Helm chart and the OpenShift operator do this for you: changing a setting or the API key rolls the pods.
For a TrueNAS certificate issued by a private CA, see
TLS and Private CAs rather than setting truenasInsecure.
| Parameter | Description | Values |
|---|---|---|
protocol |
Storage protocol | nfs, iscsi, nvmeof |
pool |
ZFS pool (overrides default) | pool name |
datasetPath |
Parent 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/iscsi → pool/k8s/iscsi/<pvc-name> |
relative path |
compression |
ZFS compression algorithm | OFF, LZ4, GZIP[-1|-9], ZSTD[-1..-9], ZLE, LZJB |
sync |
ZFS sync mode | STANDARD, ALWAYS, DISABLED |
sparse |
Thin-provision the ZVOL (iSCSI/NVMe-oF); default false |
true, 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.
| Parameter | Description | Example |
|---|---|---|
nfs.hosts |
Allowed hosts | 10.0.0.0/8,192.168.1.0/24 |
nfs.networks |
Allowed networks | 10.0.0.0/8 |
nfs.mountOptions |
Client mount options | hard,nfsvers=4.1 |
nfs.mapAllUser |
NFS user mapping (default: root) |
postgres |
nfs.mapAllGroup |
NFS group mapping (default: wheel) |
postgres |
nfs.rootSquash |
Squash 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.
| Parameter | Description | Values |
|---|---|---|
volblocksize |
ZVOL block size | 512, 1K, 2K, 4K, 8K, 16K, 32K, 64K, 128K |
iscsi.blocksize |
iSCSI logical block size | 512, 1024, 2048, 4096 |
iscsi.iqn-base |
Override the IQN base (auto-derived from the appliance's iscsi.global.basename by default) |
IQN string |
iscsi.initiators |
Allowed initiator IQNs | comma-separated |
iscsi.chapUser |
CHAP username | string |
iscsi.chapSecret |
CHAP password (12-16 chars) | string |
iscsi.chapPeerUser |
Mutual CHAP: the user the target authenticates as in return. Requires iscsi.chapUser |
string |
iscsi.chapPeerSecret |
Mutual CHAP peer password (12-16 chars, different from iscsi.chapSecret) |
string |
iscsi.multipathEnabled |
Enable multipath for the session (node-side); default false |
true, false |
iscsi.persistentSessions |
Keep the iSCSI session persistent (node-side); default false |
true, false |
IPv4 only: iSCSI portals must be IPv4. The pinned
csi-lib-iscsimis-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.
CHAP: each volume gets its own auth group on TrueNAS, which is removed with the volume. The driver never turns on discovery authentication, and nodes log in without SendTargets discovery, so discovery authentication configured on the appliance (Shares > iSCSI > Authorized Access) does not affect volumes. Without it, any initiator that can reach the portal can list the target names, though it cannot log in to a target that requires CHAP. To stop that, turn discovery authentication on there; it applies to every initiator using the appliance.
NVMe-oF also uses the volblocksize parameter above. DH-CHAP authentication is optional.
| Parameter | Description | Values |
|---|---|---|
nvmeof.hostNQN |
Authorized host NQN (required for DH-CHAP) | nqn.2014-08.org.nvmexpress:uuid:... |
nvmeof.dhchapKey |
DH-CHAP host key | DHHC-1:00:... |
nvmeof.dhchapCtrlKey |
Mutual DH-CHAP controller key | DHHC-1:00:... |
nvmeof.dhchapHash |
DH-CHAP hash (default SHA-256) |
SHA-256, SHA-384, SHA-512 |
nvmeof.dhchapDHGroup |
DH group | 2048-BIT, 3072-BIT, 4096-BIT, 6144-BIT, 8192-BIT |
| Parameter | Description | Values |
|---|---|---|
snapshot.schedule |
Cron schedule (5 fields) | 0 0 * * * |
snapshot.retention |
Retention period | 1-365 |
snapshot.retentionUnit |
Retention unit | HOUR, DAY, WEEK, MONTH, YEAR |
snapshot.naming |
Naming schema | auto-%Y-%m-%d_%H-%M |
snapshot.recursive |
Include child datasets | true, false |
| Parameter | Description | Values |
|---|---|---|
encryption |
Enable encryption | true, false |
encryption.algorithm |
Encryption algorithm | AES-256-GCM, AES-128-CCM |
encryption.passphrase |
Passphrase (min 8 chars) | string |
encryption.key |
Hex-encoded key (64 chars) | string |
encryption.generateKey |
Auto-generate key | true, false |
See the examples/ folder for sample configurations:
storageclass-nfs.yaml- Basic NFS StorageClassstorageclass-nfs-compressed.yaml- NFS with ZSTD compressionstorageclass-nfs-fsgroup.yaml- NFS for ownership-sensitive non-root workloads (no_root_squash + pod fsGroup)storageclass-iscsi.yaml- Basic iSCSI StorageClassstorageclass-iscsi-chap.yaml- iSCSI with CHAP authenticationstorageclass-nvmeof.yaml- Basic NVMe-oF/TCP StorageClassstorageclass-nvmeof-dhchap.yaml- NVMe-oF with DH-CHAP authenticationstorageclass-encrypted.yaml- Encrypted storagepvc-nfs.yaml/pvc-iscsi.yaml/pvc-nvmeof.yaml- PVC examplespod-with-pvc.yaml- Pod using a PVCvolumesnapshotclass.yaml/volumesnapshot.yaml- Snapshot examples
make build# Build Alpine-based image (standard Kubernetes)
make docker-build
# Build UBI-based image (Red Hat OpenShift certification)
make build-ubi# 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-allmake test| Image | Description |
|---|---|
ghcr.io/truenas/truenas-csi |
CSI driver (Alpine-based, for standard Kubernetes) |
quay.io/truenas_solutions/truenas-csi |
CSI driver (UBI-based, for Red Hat OpenShift) |
quay.io/truenas_solutions/truenas-csi-operator |
Kubernetes operator |
quay.io/truenas_solutions/truenas-csi-operator-bundle |
OLM bundle for OperatorHub |
For an interactive demonstration of all driver features using a local Kind cluster, see docs/demo.md.
The TrueNAS CSI Driver supports Red Hat OpenShift 4.20+ and is designed for OperatorHub distribution.
-
Install via OperatorHub
- Navigate to Operators > OperatorHub
- Search for "TrueNAS CSI"
- Click Install
-
Create credentials secret
apiVersion: v1 kind: Secret metadata: name: truenas-api-credentials namespace: truenas-csi stringData: api-key: "YOUR-API-KEY"
-
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"
- Installation Guide - Detailed installation steps
- Configuration Reference - CRD and StorageClass options
- Upgrade Guide - Upgrade procedures
- Cluster Setup Guide - Set up an OpenShift cluster on vSphere (agent-based install) for testing/certification
- Red Hat Certification Guide - Certification process and requirements
Interactive demo scripts are provided to test the CSI driver:
# Set TrueNAS connection details in deploy/truenas-csi-driver.yaml, then:
./demo-simple.sh# 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.shBoth demos provide interactive menus to test NFS/iSCSI provisioning, volume expansion, snapshots, and cloning.
- Report issues: https://github.com/truenas/truenas-csi/issues
- Submit pull requests: https://github.com/truenas/truenas-csi/pulls
GNU General Public License 3.0