A StorageClass in Kubernetes is a way to define different types of storage that can be dynamically provisioned for PersistentVolumeClaims (PVCs). Each StorageClass can represent different storage backends (e.g., local SSDs, NFS, Ceph, GlusterFS, AWS EBS, Azure Disk, GCP Persistent Disk, MinIO). Without a StorageClass, You would have to manually create PersistentVolumes (PVs) before apps can claim them. Using storage class saves time, reduces manual errors, and makes storage management flexible and scalable.
Before we go any further in Kubernetes subjects, It would be good to create a NFS StorageClass that serves persitent disk space for our pods. NFS can provide both ReadWriteOnce and ReadWriteMany capabilities and its widely used. I will install NFS server role on a Ubuntu VM. NFS mount point will be /mnt/nfs.
Ubuntu NFS Server IP Address: 192.168.204.29
NFS Mount Point: /mnt/nfs
K8s Worker Nodes' Subnet: 192.168.204.0/24
apt update
apt install nfs-kernel-server -y
mkdir -p /mnt/nfs
chown nobody:nogroup /mnt/nfs
chmod 777 /mnt/nfs
nano /etc/exports
#Add the below wntry in /etc/exports
/mnt/nfs 192.168.204.0/24(rw,sync,no_subtree_check,no_root_squash)
exportfs -rav
systemctl restart nfs-kernel-server
On Kubernetes Worker nodes, intsall nfs-utils
dnf install -y nfs-utils
#The command below will test the connection to nfs server. If there is no error, NFS server is successfully configured.
mount -t nfs -o nfsvers=3 192.168.204.29:/mnt/nfs /mnt
StorageClass:
On our Kubernetes Management Computer (in my case, my laptop), run the following. It downloads and applies the RBAC (permissions) manifest for the NFS Subdir External Provisioner. In other words, it sets up the service account and permissions the provisioner needs to create/delete PersistentVolumes on your cluster.
kubectl apply -f https://raw.githubusercontent.com/kubernetes-sigs/nfs-subdir-external-provisioner/master/deploy/rbac.yaml
Create a file named "nfs-client-deploy.yaml" with the following content
apiVersion: apps/v1
kind: Deployment
metadata:
name: nfs-client-provisioner
namespace: default
spec:
replicas: 1
selector:
matchLabels:
app: nfs-client-provisioner
template:
metadata:
labels:
app: nfs-client-provisioner
spec:
serviceAccountName: nfs-client-provisioner
containers:
- name: nfs-client-provisioner
image: registry.k8s.io/sig-storage/nfs-subdir-external-provisioner:v4.0.2
volumeMounts:
- name: nfs-client-root
mountPath: /persistentvolumes
env:
- name: PROVISIONER_NAME
value: nfs-client
- name: NFS_SERVER
value: 192.168.204.29
- name: NFS_PATH
value: /mnt/nfs
volumes:
- name: nfs-client-root
nfs:
server: 192.168.204.29
path: /mnt/nfs
Then apply it
kubectl apply -f nfs-client-deploy.yaml
This yaml file deploys the NFS Subdir External Provisioner so your cluster can dynamically provision PersistentVolumes on an NFS share

Create a yaml file named "nfs-client-storageclass.yaml" with the following content. This yaml file defines a StorageClass for dynamic NFS volumes provided by your NFS Subdir External Provisioner.
Dynamically created PVs from this class will have ReclaimPolicy=Delete. When the claim is removed, the PV object is deleted and the provisioner cleans up storage
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
name: nfs-client
provisioner: nfs-client
parameters:
archiveOnDelete: "false"
reclaimPolicy: Delete
volumeBindingMode: Immediate
kubectl apply -f nfs-client-storageclass.yaml

We just created our first StorageClass above. According to your disk types and speed you can have multiple StorageClasses and assign them to your Pods accordingly. If you have multiple StorageClass, you might want to choose one of them as the default StorageClass. I just added annotations to "nfs-client-storageclass.yaml" and applied it again.
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
name: nfs-client
annotations:
storageclass.kubernetes.io/is-default-class: "true"
provisioner: nfs-client
parameters:
archiveOnDelete: "false"
reclaimPolicy: Delete
volumeBindingMode: Immediate
kubectl apply -f nfs-client-storageclass.yaml
Let us see details for this StorageClass

Now we can create a PVC and use the StorageClass we created above. I will create 2 PVCs to demostrate different modes (RWO & RWX)
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: claim1
spec:
accessModes:
- ReadWriteOnce
storageClassName: nfs-client
resources:
requests:
storage: 1Gi
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: claim2
spec:
accessModes:
- ReadWriteMany
storageClassName: nfs-client
resources:
requests:
storage: 500Mi
If we delete PVCs, PVs will be deleted automatically.
