nfs-subdir-external-provisioner: dynamic PVCs on an NFS share you already run
Dynamic sub-dir volume provisioner on a remote NFS server.
At a glance
- What is it?
- The SIG Storage provisioner turns an existing NFS export into a StorageClass that creates one directory per PersistentVolumeClaim. It is a small controller with a narrow job, and its limits are as clear as its scope.
- Who is it for?
- Adopt nfs-subdir-external-provisioner when you already run an NFS server, want ReadWriteMany volumes without a CSI driver, and accept that the volume is a directory on a share with no capacity enforcement. Do not adopt it as a general storage layer: if you need snapshots, cloning, quotas or per-volume encryption, an NFS CSI driver is the wrong shape and so is this.
- Can I use it commercially?
- Yes. Apache-2.0 is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
- Is it still maintained?
- Activity is slowing. The repository last received commits 6 months ago.
- What is it written in?
- Mainly Shell, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on September 29, 2026, and from our analysis. They are not legal advice.
Editorial analysis
The gap it fills: PVCs on an NFS share you already have
Kubernetes dynamic provisioning needs a storage backend that can create volumes on demand. NFS servers cannot do that on their own: they export a directory, and nothing in the protocol creates a per-claim subdirectory when a PersistentVolumeClaim appears. The usual workaround is static provisioning, where an administrator pre-creates PersistentVolumes that match the exports and hands them out. That does not scale past a handful of claims, and it leaves the cluster with no ReadWriteMany option unless a CSI driver is installed.
nfs-subdir-external-provisioner sits in that gap. It is a controller that watches PersistentVolumeClaims and, for each one bound to its StorageClass, creates a directory on an existing NFS export and registers a PersistentVolume pointing at it. The README is explicit that the server must already exist and be configured: this project does not deploy or manage NFS. It is for cluster operators who have an NFS export, want claims to provision themselves, and do not want to run a full CSI stack for it.
How the controller names directories and binds claims
The naming scheme is the most concrete thing in the README: persistent volumes are provisioned as `${namespace}-${pvcName}-${pvName}`. That string becomes a directory under the export path, so the server's filesystem layout maps one-to-one onto cluster objects. An operator can look at the share and tell which namespace and which claim owns a directory without querying the API.
The controller is built on sigs.k8s.io/sig-storage-lib-external-provisioner/v6, the shared library for out-of-tree provisioners, and go.mod pins k8s.io/client-go v0.35.1 alongside it. That library supplies the claim watch, the provisioning loop and the delete path; this project supplies the NFS-specific behaviour. The container itself is minimal: the Dockerfile starts from gcr.io/distroless/static and copies a single binary at /nfs-subdir-external-provisioner, so there is no shell and no package manager inside the image. The Deployment mounts the NFS export into the pod as nfs-client-root, and the provisioner creates and removes subdirectories through that mount. The build targets linux on amd64, arm, arm64, ppc64le and s390x, according to the Makefile.
Installing it with the Helm chart and testing a claim
The README gives Helm as the short path. The chart lives in the repository under charts/nfs-subdir-external-provisioner and is published to a GitHub Pages repository. Two values carry the connection information: nfs.server and nfs.path.
helm repo add nfs-subdir-external-provisioner https://kubernetes-sigs.github.io/nfs-subdir-external-provisioner/
helm install nfs-subdir-external-provisioner nfs-subdir-external-provisioner/nfs-subdir-external-provisioner \
--set nfs.server=x.x.x.x \
--set nfs.path=/exported/pathReplace x.x.x.x with the server address and /exported/path with the exported share. After the release installs, the cluster has a StorageClass named nfs-client. That name is the default, and the README shows how to patch it if you want something else.
For a Kustomize deployment, the README adds the deploy directory as a base and patches the container environment. The patch sets NFS_SERVER and NFS_PATH on the nfs-client-provisioner container and rewrites the nfs-client-root volume to point at the same server and path.
apiVersion: apps/v1
kind: Deployment
metadata:
labels:
app: nfs-client-provisioner
name: nfs-client-provisioner
spec:
template:
spec:
containers:
- name: nfs-client-provisioner
env:
- name: NFS_SERVER
value: <YOUR_NFS_SERVER_IP>
- name: NFS_PATH
value: <YOUR_NFS_SERVER_SHARE>With the provisioner running, the README's test applies a claim and a pod that writes a file. The check is on the server side: look inside the new directory for a file named SUCCESS. Deleting the same two manifests should remove the directory again. That round trip is the fastest way to confirm both provisioning and deletion work.
kubectl create -f https://raw.githubusercontent.com/kubernetes-sigs/nfs-subdir-external-provisioner/master/deploy/test-claim.yaml -f https://raw.githubusercontent.com/kubernetes-sigs/nfs-subdir-external-provisioner/master/deploy/test-pod.yaml
kubectl delete -f https://raw.githubusercontent.com/kubernetes-sigs/nfs-subdir-external-provisioner/master/deploy/test-claim.yaml -f https://raw.githubusercontent.com/kubernetes-sigs/nfs-subdir-external-provisioner/master/deploy/test-pod.yamlThe manual path is longer but does not depend on Helm or Kustomize. You clone the repository, edit deploy/rbac.yaml and deploy/deployment.yaml, and set the RBAC subject to the namespace where the provisioner runs. The README's snippet derives that namespace from the current kubectl context and rewrites both files with sed before applying the RBAC manifest. OpenShift users should note the README's warning that on some installations the default admin user lacks cluster-admin rights, in which case those commands fail and the permissions have to be granted separately.
What the provisioner does not do: quotas, snapshots, capacity
The subdirectory is the volume. Nothing in the README suggests the provisioner enforces a size limit on it, and NFS itself has no per-directory quota mechanism the controller could use. A claim asking for 10Gi and a claim asking for 1Ti both end up as directories on the same export, drawing from the same underlying filesystem. Capacity planning happens on the server, not in the StorageClass.
Snapshots and cloning are absent from the documented surface. The provisioner creates and deletes directories; it has no snapshot class, no restore path and no volume cloning. The README also notes that automated end-to-end tests are a pending area of development, so the test coverage you get is the manual claim-and-pod round trip above rather than a CI suite you can trust to catch regressions. The last release in the repository is 4.0.18 from 2023-03-13, while the last push to master was on 2026-03-31, so the image tag you install from a release is not the same code as the branch. There is also no documented rollback procedure if a provisioner upgrade goes wrong, and no documented migration path for volumes created by an older version.
nfs-subdir-external-provisioner or an NFS CSI driver
The natural comparison is the NFS CSI driver, which implements the Container Storage Interface instead of the older external-provisioner library. The difference is not cosmetic. CSI drivers expose capabilities through the CSI spec: snapshots via VolumeSnapshot, volume cloning, and per-volume parameters that the driver can act on. This provisioner predates that model and speaks the Kubernetes PV/PVC API directly through the external-provisioner library, so anything the CSI spec adds has no equivalent here.
The trade-off runs the other way too. A CSI driver is another component to deploy, upgrade and debug, with its own node plugin on every node. This provisioner is a single Deployment that mounts one NFS export and creates directories. If your requirement is exactly that, the CSI path adds moving parts you do not need. If snapshots, cloning or a supported upgrade path matter, the CSI driver is the one to pick, and the README offers no argument against that.
Maintenance status, licensing and upgrade cost
The repository is not archived, and the last push was on 2026-03-31. The latest tagged release, 4.0.18, dates from 2023-03-13, so the gap between releases and branch activity is wide. That matters for upgrades: pulling the published image gets you release code, not the master branch, and the CHANGELOG.md at the repository root is where release notes live. Nothing in the README describes a supported upgrade procedure for the provisioner itself, so an upgrade is a redeploy of the Deployment or Helm release.
The project is licensed under Apache-2.0, with the standard header reproduced in the Dockerfile and Makefile. Apache-2.0 permits commercial use and modification and includes a patent grant; it also requires that you keep the license and attribution notices. If you vendor the manifests or build the binary yourself, that obligation travels with the code. This is a description of the license text, not legal advice, and any distribution question belongs with your own counsel. On the operational side, the cost of running it is low: one Deployment, one StorageClass, and the NFS mount. The cost of getting it wrong is on the server, because the export is shared by every claim.
Editorial conclusion
Adopt nfs-subdir-external-provisioner when you already run an NFS server, want ReadWriteMany volumes without a CSI driver, and accept that the volume is a directory on a share with no capacity enforcement. Do not adopt it as a general storage layer: if you need snapshots, cloning, quotas or per-volume encryption, an NFS CSI driver is the wrong shape and so is this. Before rollout, verify that the cluster nodes can reach the server, that the export is writable by the provisioner pod, and that a test PVC leaves a SUCCESS file in the expected directory on the server.
Frequently asked questions
How do I install nfs-subdir-external-provisioner?
The README gives a Helm install from the project's chart repository with nfs.server and nfs.path set to your existing NFS server, or a Kustomize deployment that adds the deploy directory as a base and patches NFS_SERVER and NFS_PATH into the nfs-client-provisioner container.
What is nfs-subdir-external-provisioner?
It is an automatic provisioner that uses an existing, already configured NFS server to support dynamic provisioning of Kubernetes PersistentVolumes through PersistentVolumeClaims. Each volume is provisioned as a directory named ${namespace}-${pvcName}-${pvName}.
How do I use the NFS server provisioner in Kubernetes?
You must already have an NFS server. Deploy the provisioner, then create PersistentVolumeClaims against the StorageClass it installs, named nfs-client by default, and the provisioner creates the matching directory on the export.
What ports should I open for NFS mounts?
The README does not list NFS ports. It only states that the NFS server must be accessible from your Kubernetes cluster, so port configuration comes from your NFS server setup rather than this project.
Official sources
Add this badge to your README
If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.
[](https://hysenlabs.com/projects/kubernetes-sigs-nfs-subdir-external-provisioner)