In this series of articles, I will discuss available Kubernetes storage solutions with the complete manual for deploying them to/for Kubernetes. This series of tutorials is helpful for everyone who knows the Kubernetes storage architecture and concepts and wants to deploy storage for Stateful applications in Kubernetes.

Follow our social media:

Resource Requirements:

  • A running Kubernetes cluster. 1.18+ is suggested.
  • A running node with some available storage.

I will run the steps on Ubuntu-based systems. I suggest you do so.

For this demonstration, I use two nodes with the following configuration:

A running node used as the NFS server:

OS: Ubuntu 20.04
FQDN: node004.b9tcluster.local
IP Address:

A single-node Kubernetes cluster:

OS: Ubuntu 20.04
Kubernetes: 1.21.5 (k3s distribution)
FQDN: node005.b9tcluster.local
IP Address:

1- Deploy and configure NFS server:

Run the following commands on the node you considered for the NFS server. You can deploy a clustered NFS server with high availability support.

apt update && apt -y upgrade

apt install -y nfs-server

mkdir /data

cat <<EOF >> /etc/exports

systemctl enable --now nfs-server

exportfs -ar

These commands install NFS server and export /data , which is accessible by the Kubernetes cluster. In the case of a multi-node Kubernetes cluster, you should allow all Kubernetes worker nodes.

2- Prepare Kubernetes worker nodes:

Now, to connect to the NFS server, the Kubernetes nodes need the NFS client package. You should run the following command only on the Kubernetes worker nodes – and control-plane nodes if they act as workers too.

apt install -y nfs-common

Important tip! Each storage solution may need client packages to connect to the storage server. You should install them in all Kubernetes worker nodes.

For NFS, the nfs-common package is required.

3- Using NFS in Kubernetes:

Method 1 — Connecting to NFS directly with Pod manifest:

To connect to the NFS storage directly using the Pod manifest, use the NFSVolumeSource in the PodSpec. Here is an example:

apiVersion: v1
kind: Pod
  name: test
  labels: alpine kubernetes-complete-reference ssbostan
    - name: alpine
      image: alpine:latest
        - touch
        - /data/test
        - name: nfs-volume
          mountPath: /data
    - name: nfs-volume
        server: node004.b9tcluster.local
        path: /data
        readOnly: no

Method 2 — Connecting using the PersistentVolume resource:

Use the following manifest to create the PersistentVolume object for the NFS volume. You should note that the storage size does not take any effect.

apiVersion: v1
kind: PersistentVolume
  name: nfs-volume
  labels: nfs kubernetes-complete-reference ssbostan
    - ReadWriteOnce
    - ReadOnlyMany
    - ReadWriteMany
    storage: 10Gi
  storageClassName: ""
  persistentVolumeReclaimPolicy: Recycle
  volumeMode: Filesystem
    server: node004.b9tcluster.local
    path: /data
    readOnly: no

Method 3 — Dynamic provisioning using StorageClass:

You must install the NFS provisioner to provision PersistentVolume dynamically using StorageClasses. I use the nfs-subdir-external-provisioner to achieve this. The following commands install everything we need using the Helm package manager.

helm repo add nfs-subdir-external-provisioner

helm install nfs-subdir-external-provisioner nfs-subdir-external-provisioner/nfs-subdir-external-provisioner \
  --create-namespace \
  --namespace nfs-provisioner \
  --set nfs.server=node004.b9tcluster.local \
  --set nfs.path=/data

To create the PersistentVolumeClaim, use the following manifest:

apiVersion: v1
kind: PersistentVolumeClaim
  name: nfs-test
  labels: nfs kubernetes-complete-reference ssbostan
    - ReadWriteMany
  storageClassName: nfs-client
      storage: 1Gi

4- Kubernetes and NFS storage specification:

NFS has the following specifications in the Kubernetes world. It would help to consider them before using the NFS storage in production.

  • ReadWriteOnce, ReadOnlyMany, and ReadWriteMany access modes.
  • The storage size does not take any effect!
  • In the case of dynamic provisioning, volumes are separated into different directories but without access controls or proper isolation.

If you like this series of articles, please share them and write your thoughts as comments here. Your feedback encourages me to complete this massively planned program.

Follow my LinkedIn

Follow Kubedemy LinkedIn

Follow Kubedemy Telegram

Leave a Reply

Your email address will not be published. Required fields are marked *