You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
Kubernetes Prow Robot b8c5a755f1
Merge pull request #199 from humblec/flaky
7 days ago
.github/workflows Fix the helm chart linter test failure by disabling maintainers validation and remove trailing spaces 1 year ago
charts/nfs-subdir-external-provisioner Update 3 months ago
cmd/nfs-subdir-external-provisioner build: import GetPersistentVolumeClass from component-helpers 2 months ago
deploy Merge branch 'master' of into develop 4 months ago
docker move to alpine 3.12 for base image 2 years ago
release-tools Merge commit '9ee89eec502067088a7775321af5040f57e894fb' into prow-update-master 1 month ago
vendor build: import GetPersistentVolumeClass from component-helpers 2 months ago Update cloudbuild with multiarch platforms 1 year ago
.gitignore nfs-client-provisioner initial code 2 years ago Bump version to 4.0.2 1 year ago Update template files to include repo-specific info 2 years ago
Dockerfile set Dockerfile to build from distroless 1 year ago
Dockerfile.multiarch Add GitAction 1 year ago
LICENSE Initial commit 2 years ago
Makefile remove BUILD_PLATFORM variable setting from the makefile 7 days ago
OWNERS update OWNERS file 3 months ago
OWNERS_ALIASES OWNERS: symlink aliases from release-tools 8 months ago docs: consistent StorageClass names, multiple provisioners installation 4 months ago Initial commit 2 years ago
SECURITY_CONTACTS Update template files to include repo-specific info 2 years ago
cloudbuild.yaml setup automated builds using the release-tools 1 year ago Initial commit 2 years ago
go.mod build: import GetPersistentVolumeClass from component-helpers 2 months ago
go.sum build: import GetPersistentVolumeClass from component-helpers 2 months ago

Kubernetes NFS Subdir External Provisioner

NFS subdir external provisioner is an automatic provisioner that use your existing and already configured NFS server to support dynamic provisioning of Kubernetes Persistent Volumes via Persistent Volume Claims. Persistent volumes are provisioned as ${namespace}-${pvcName}-${pvName}.

Note: This repository is migrated from As part of the migration:

  • The container image name and repository has changed to and nfs-subdir-external-provisioner respectively.
  • To maintain backward compatibility with earlier deployment files, the naming of NFS Client Provisioner is retained as nfs-client-provisioner in the deployment YAMLs.
  • One of the pending areas for development on this repository is to add automated e2e tests. If you would like to contribute, please raise an issue or reach us on the Kubernetes slack #sig-storage channel.

How to deploy NFS Subdir External Provisioner to your cluster

To note again, you must already have an NFS Server.

With Helm

Follow the instructions from the helm chart README.

The tl;dr is

$ helm repo add 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/path

Without Helm

Step 1: Get connection information for your NFS server

Make sure your NFS server is accessible from your Kubernetes cluster and get the information you need to connect to it. At a minimum you will need its hostname.

Step 2: Get the NFS Subdir External Provisioner files

To setup the provisioner you will download a set of YAML files, edit them to add your NFS server's connection information and then apply each with the kubectl / oc command.

Get all of the files in the deploy directory of this repository. These instructions assume that you have cloned the kubernetes-sigs/nfs-subdir-external-provisioner repository and have a bash-shell open in the root directory.

Step 3: Setup authorization

If your cluster has RBAC enabled or you are running OpenShift you must authorize the provisioner. If you are in a namespace/project other than "default" edit deploy/rbac.yaml.


# Set the subject of the RBAC objects to the current namespace where the provisioner is being deployed
$ NS=$(kubectl config get-contexts|grep -e "^\*" |awk '{print $5}')
$ NAMESPACE=${NS:-default}
$ sed -i'' "s/namespace:.*/namespace: $NAMESPACE/g" ./deploy/rbac.yaml ./deploy/deployment.yaml
$ kubectl create -f deploy/rbac.yaml


On some installations of OpenShift the default admin user does not have cluster-admin permissions. If these commands fail refer to the OpenShift documentation for User and Role Management or contact your OpenShift provider to help you grant the right permissions to your admin user. On OpenShift the service account used to bind volumes does not have the necessary permissions required to use the hostmount-anyuid SCC. See also Role based access to SCC for more information. If these commands fail refer to the OpenShift documentation for User and Role Management or contact your OpenShift provider to help you grant the right permissions to your admin user.

# Set the subject of the RBAC objects to the current namespace where the provisioner is being deployed
$ NAMESPACE=`oc project -q`
$ sed -i'' "s/namespace:.*/namespace: $NAMESPACE/g" ./deploy/rbac.yaml ./deploy/deployment.yaml
$ oc create -f deploy/rbac.yaml
$ oc adm policy add-scc-to-user hostmount-anyuid system:serviceaccount:$NAMESPACE:nfs-client-provisioner

Step 4: Configure the NFS subdir external provisioner

If you would like to use a custom built nfs-subdir-external-provisioner image, you must edit the provisioner's deployment file to specify the correct location of your nfs-client-provisioner container image.

Next you must edit the provisioner's deployment file to add connection information for your NFS server. Edit deploy/deployment.yaml and replace the two occurences of with your server's hostname.

kind: Deployment
apiVersion: apps/v1
  name: nfs-client-provisioner
  replicas: 1
      app: nfs-client-provisioner
    type: Recreate
        app: nfs-client-provisioner
      serviceAccountName: nfs-client-provisioner
        - name: nfs-client-provisioner
            - name: nfs-client-root
              mountPath: /persistentvolumes
            - name: PROVISIONER_NAME
            - name: NFS_SERVER
              value: <YOUR NFS SERVER HOSTNAME>
            - name: NFS_PATH
              value: /var/nfs
        - name: nfs-client-root
            server: <YOUR NFS SERVER HOSTNAME>
            path: /var/nfs

Note: If you want to change the PROVISIONER_NAME above from to something else like myorg/nfs-storage, remember to also change the PROVISIONER_NAME in the storage class definition below.

To disable leader election, define an env variable named ENABLE_LEADER_ELECTION and set its value to false.

Step 5: Deploying your storage class


Name Description Default
onDelete If it exists and has a delete value, delete the directory, if it exists and has a retain value, save the directory. will be archived with name on the share: archived-<volume.Name>
archiveOnDelete If it exists and has a false value, delete the directory. if onDelete exists, archiveOnDelete will be ignored. will be archived with name on the share: archived-<volume.Name>
pathPattern Specifies a template for creating a directory path via PVC metadata's such as labels, annotations, name or namespace. To specify metadata use ${.PVC.<metadata>}. Example: If folder should be named like <pvc-namespace>-<pvc-name>, use ${.PVC.namespace}-${} as pathPattern. n/a

This is deploy/class.yaml which defines the NFS subdir external provisioner's Kubernetes Storage Class:

kind: StorageClass
  name: nfs-client
provisioner: # or choose another name, must match deployment's env PROVISIONER_NAME'
  pathPattern: "${.PVC.namespace}/${}" # waits for annotation, if not specified will accept as empty string.
  onDelete: delete

Step 6: Finally, test your environment!

Now we'll test your NFS subdir external provisioner.


$ kubectl create -f deploy/test-claim.yaml -f deploy/test-pod.yaml

Now check your NFS Server for the file SUCCESS.

kubectl delete -f deploy/test-pod.yaml -f deploy/test-claim.yaml

Now check the folder has been deleted.

Step 7: Deploying your own PersistentVolumeClaims

To deploy your own PVC, make sure that you have the correct storageClassName as indicated by your deploy/class.yaml file.

For example:

kind: PersistentVolumeClaim
apiVersion: v1
  name: test-claim
  annotations: "test-path" # not required, depending on whether this annotation was shown in the storage class description
  storageClassName: nfs-client
    - ReadWriteMany
      storage: 1Mi

Build and publish your own container image

To build your own custom container image from this repository, you will have to build and push the nfs-subdir-external-provisioner image using the following instructions.

make build
make container
# `nfs-subdir-external-provisioner:latest` will be created. 
# Note: This will build a single-arch image that matches the machine on which container is built.
# To upload this to your custom registry, say `` and arch as amd64, you can use
# docker tag nfs-subdir-external-provisioner:latest
# docker push

Build and publish with GitHub Actions

In a forked repository you can use GitHub Actions pipeline defined in .github/workflows/release.yml. The pipeline builds Docker images for linux/amd64, linux/arm64, and linux/arm/v7 platforms and publishes them using a multi-arch manifest. The pipeline is triggered when you add a tag like gh-v{major}.{minor}.{patch} to your commit and push it to GitHub. The tag is used for generating Docker image tags: latest, {major}, {major}:{minor}, {major}:{minor}:{patch}.

The pipeline adds several labels:

  • org.opencontainers.image.title=${{ }}
  • org.opencontainers.image.description=${{ github.event.repository.description }}
  • org.opencontainers.image.url=${{ github.event.repository.html_url }}
  • org.opencontainers.image.source=${{ github.event.repository.clone_url }}
  • org.opencontainers.image.created=${{ steps.prep.outputs.created }}
  • org.opencontainers.image.revision=${{ github.sha }}
  • org.opencontainers.image.licenses=${{ github.event.repository.license.spdx_id }}


  • The pipeline performs the docker login command using REGISTRY_USERNAME and REGISTRY_TOKEN secrets, which have to be provided.
  • You also need to provide the DOCKER_IMAGE secret specifying your Docker image name, e.g.,[username]/nfs-subdir-external-provisioner.

NFS provisioner limitations/pitfalls

  • The provisioned storage is not guaranteed. You may allocate more than the NFS share's total size. The share may also not have enough storage space left to actually accommodate the request.
  • The provisioned storage limit is not enforced. The application can expand to use all the available storage regardless of the provisioned size.
  • Storage resize/expansion operations are not presently supported in any form. You will end up in an error state: Ignoring the PVC: didn't find a plugin capable of expanding the volume; waiting for an external controller to process this PVC.