MYHOSPITALNOW.COM

Find Trusted Doctors & Hospitals

Discover healthcare providers, compare hospitals, and explore treatment options worldwide.

Free Treatment Quotes • Doctor Search • Hospital Comparison

Find Healthcare Options

www.myhospitalnow.com

Kubernetes storageclass using OpenEBS Local PV HostPath — Complete Step-by-Step Kubernetes Tutorial

Dynamic storage provisioner: openebs.io/local
Lab platform: Self-managed Kubernetes v1.35.10 on Ubuntu Linux (kubeadm or other manual installation)
OpenEBS release referenced: OpenEBS 4.6.2 (HostPath engine v4.6.0), current as checked on November 12, 2026
Lab target: Three StatefulSet replicas, each with its own dynamically provisioned 1Gi Local PV backed by a directory on a worker node
Difficulty: Beginner to intermediate
Scope: A lightweight OpenEBS Local PV HostPath-only installation; no EKS, AKS, GKE, Ceph, LVM, ZFS, or Mayastor required.

Important: All commands are examples to run against your cluster, not commands already executed. Verify your kubectl context and node status before applying anything. A 1Gi PVC represents a requested size; without filesystem quotas it does not impose a 1Gi disk-use limit. The backing storage is node-local, not replicated or highly available.


Table of contents

  1. Learning objectives
  2. Architecture and terminology
  3. OpenEBS vs Rancher vs Kubernetes no-provisioner
  4. Prerequisites and safety checks
  5. Install Helm if needed
  6. Install OpenEBS HostPath-only using Helm
  7. Check the default OpenEBS StorageClass
  8. Create a custom HostPath StorageClass
  9. Create a StatefulSet with automatic PVCs
  10. Apply resources and verify dynamic provisioning
  11. Find the real data directory on the worker node
  12. Test persistence after Pod recreation
  13. Test storage isolation across replicas
  14. Optional: Headless Service for stable DNS
  15. Advanced configuration
  16. Troubleshooting common errors
  17. Storage lifecycle, retention, and cleanup
  18. Operational considerations and limitations
  19. Frequently asked questions
  20. Command cheat sheet
  21. Official references

1. Learning objectives

At the end of this lab, you will be able to:

  • Install the OpenEBS Local PV HostPath dynamic provisioner on a self-managed Ubuntu cluster.
  • Understand openebs.io/local and WaitForFirstConsumer.
  • Create only a StorageClass and StatefulSet as application manifests. OpenEBS and the StatefulSet will create PVs and PVCs automatically.
  • Provision three independent 1Gi volumes for three StatefulSet Pods.
  • Locate and inspect the directories on the underlying worker node.
  • Confirm that files survive Pod deletion and recreation on the same node.
  • Troubleshoot Pending PVCs, taints, provisioning problems, and permission failures.
  • Clean up safely without accidentally deleting other storage workloads.

2. Architecture and terminology

2.1 What is OpenEBS Local PV HostPath?

OpenEBS is a Kubernetes storage project. The Local PV HostPath engine dynamically provisions persistent volumes from directories on a Kubernetes node’s filesystem. The local disk may be the OS disk or another disk already mounted on the node.

Provisioning flow:

  StorageClass: openebs-hostpath-lab
       provisioner: openebs.io/local
       volumeBindingMode: WaitForFirstConsumer
                        |
                        v
               StatefulSet (3 Pods)
                        |
       +----------------+----------------+
       |                |                |
       v                v                v
   Pod ...-0         Pod ...-1         Pod ...-2
       |                |                |
    PVC ...-0         PVC ...-1         PVC ...-2
       |                |                |
       v                v                v
  OpenEBS provisions three local PVs dynamically
       |                |                |
       v                v                v
 /var/openebs/local/<PV-0>  .../<PV-1>  .../<PV-2>
 (on the node where the corresponding Pod runs)

There is no shared network volume in this model. The three backing directories may be on the same worker or on different workers, depending on scheduling.

2.2 Key terms

TermMeaning
StorageClass (SC)Defines how storage should be provisioned.
PersistentVolumeClaim (PVC)Application request for storage. A StatefulSet’s volumeClaimTemplates generates these automatically.
PersistentVolume (PV)Cluster storage object bound to a claim; OpenEBS creates it dynamically.
HostPath base pathDirectory on the host, e.g. /var/openebs/local, under which PV directories are created.
Local PVKubernetes PV referring to a filesystem path on a specific node, with node affinity.
OpenEBS provisionerController that creates/deletes PV objects and manages backing directories.
WaitForFirstConsumerAllows the scheduler to choose the Pod’s node before local volume creation.
ReadWriteOnce (RWO)Supported access mode for OpenEBS Local PV HostPath; not a shared RWX network filesystem.

2.3 Critical distinction: HostPath-backed does not mean spec.hostPath

OpenEBS Local PV HostPath normally creates a PV with spec.local.path, not spec.hostPath.path. Its data still lives in a host filesystem directory. This is intentional: the local PV API includes node affinity, making scheduling more reliable.

To inspect a provisioned PV, query .spec.local.path:

kubectl get pv <YOUR_PV_NAME> -o jsonpath='{.spec.local.path}{"\n"}'
Code language: HTML, XML (xml)

Do not use kubectl get pv ... -o jsonpath='{.spec.hostPath.path}' for this OpenEBS engine; it will usually be empty.


3. OpenEBS vs Rancher vs Kubernetes no-provisioner

StorageClass provisionerDynamic PV creation?Host-local directory backing?Separate controller installation?
kubernetes.io/no-provisionerNoYes, with manually created local PVsNo
rancher.io/local-pathYesYesYes: Rancher Local Path Provisioner
openebs.io/localYesYesYes: OpenEBS Local PV HostPath

Why your old StorageClass stayed Pending:

provisioner: kubernetes.io/no-provisioner
volumeBindingMode: WaitForFirstConsumer
Code language: HTTP (http)

This supports static provisioning only. A matching PV must already exist. OpenEBS changes that: the controller creates PVs for qualifying PVC requests.

Merely changing a StorageClass’s provisioner to openebs.io/local without installing the OpenEBS controller will not work.


4. Prerequisites and safety checks

This tutorial assumes:

  • A functioning Kubernetes API server, scheduler, controller manager, networking, kubelet, and container runtime.
  • kubectl configured to administer the cluster.
  • At least one Ready and schedulable worker node; control-plane-only nodes may have a NoSchedule taint.
  • Disk space available on the node filesystem containing /var/openebs/local (at least several GiB for a small lab).
  • Helm 3 installed (if not, see the next section).
  • Cluster-wide rights to install CRDs, deployments, RBAC, and StorageClasses.
  • Outbound access to Helm repositories and container registries from the appropriate client/nodes, unless using a preconfigured internal mirror.

4.1 Confirm cluster access

kubectl config current-context
kubectl version
kubectl cluster-info
kubectl get nodes -o wide
kubectl get sc
Code language: JavaScript (javascript)

Review current-context carefully before installing.

4.2 Identify node taints

kubectl get nodes -o custom-columns=NAME:.metadata.name,READY:.status.conditions[-1].type,TAINTS:.spec.taints
kubectl describe nodes | grep -A 3 'Taints:'
Code language: JavaScript (javascript)

Your earlier output showed two nodes, with one node rejected because of an untolerated taint. That can be normal for a control-plane node. The remaining worker must be Ready and have adequate resources. You do not need to remove the control-plane taint for this lab.

4.3 Check free disk space on the worker

SSH or access the worker node (not just the administrative host):

df -h /var
lsblk -f
Code language: JavaScript (javascript)

If /var/openebs/local does not exist yet, OpenEBS can create it during provisioning. For a dedicated disk, mount that disk at a suitable BasePath before provisioning.

4.4 Protect existing storage

kubectl get pv
kubectl get pvc -A
kubectl get sc
helm list -A
Code language: JavaScript (javascript)

Do not delete your existing manual StorageClass, PVs, PVCs, or Rancher provisioner. This tutorial uses new resource names to coexist with them.


5. Install Helm if needed

Check whether Helm is available:

helm version

If Helm is already installed, skip this step.

For Ubuntu, follow the official Helm installation instructions at https://helm.sh/docs/intro/install/. One documented installation method is:

curl -fsSL https://raw.githubusercontent.com/helm/helm/main/scripts/get-helm-3 | bash
helm version
Code language: JavaScript (javascript)

Security note: Review the script or use your organization’s approved package-management process before executing remote scripts.


6. Install OpenEBS HostPath-only using Helm

The unified OpenEBS Helm chart installs several engines by default. A small Ubuntu lab needs only Local PV HostPath, so disable Mayastor, LVM, and ZFS. The commands below also disable optional Loki/Alloy observability components to conserve resources.

6.1 Add the official OpenEBS chart repository

helm repo add openebs https://openebs.github.io/openebs
helm repo update
helm search repo openebs/openebs --versions | head
Code language: JavaScript (javascript)

6.2 Inspect the chart before installation

As of this tutorial, the checked OpenEBS chart release is 4.6.2. Verify it is present in the repository:

helm show chart openebs/openebs --version 4.6.2
helm show values openebs/openebs --version 4.6.2 > openebs-chart-defaults.yaml

Review openebs-chart-defaults.yaml if your environment uses custom policies or registry mirrors. Pinning the chart version makes the lab more reproducible. If your chart repository exposes a different version, inspect it before adjusting the commands.

6.3 Install only Local PV HostPath

helm upgrade --install openebs openebs/openebs \
  --namespace openebs \
  --create-namespace \
  --version 4.6.2 \
  --set engines.replicated.mayastor.enabled=false \
  --set engines.local.lvm.enabled=false \
  --set engines.local.zfs.enabled=false \
  --set engines.local.rawfile.enabled=false \
  --set loki.enabled=false \
  --set alloy.enabled=false
Code language: JavaScript (javascript)

Why disable the other components? You are testing only local directory provisioning; you do not need distributed replicas, LVM volumes, ZFS pools, or a complete OpenEBS observability stack.

6.4 Verify the Helm release and controller

helm status openebs -n openebs
helm list -n openebs
kubectl get pods -n openebs -o wide
kubectl get deployments -n openebs
kubectl get storageclass
Code language: JavaScript (javascript)

Look for a deployment with a name similar to openebs-localpv-provisioner in the openebs namespace. When present, verify its rollout:

kubectl rollout status deployment/openebs-localpv-provisioner \
  -n openebs --timeout=180s

The provisioner Pod should become Running and Ready. If not, review troubleshooting.


7. Check the default OpenEBS StorageClass

OpenEBS ordinarily creates a default-named HostPath StorageClass:

kubectl get sc openebs-hostpath
kubectl describe sc openebs-hostpath
Code language: JavaScript (javascript)

Expected key properties:

NAME               PROVISIONER      RECLAIMPOLICY   VOLUMEBINDINGMODE
openebs-hostpath   openebs.io/local  Delete          WaitForFirstConsumer

The usual base directory is /var/openebs/local.

Fastest possible test: Set storageClassName: openebs-hostpath in your StatefulSet and skip creating your own StorageClass. However, the next step creates a custom StorageClass, so you can learn exactly how its annotations work.


8. Create a custom HostPath StorageClass

Save this file as sc.yaml:

apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
  name: openebs-hostpath-lab
  annotations:
    openebs.io/cas-type: local
    cas.openebs.io/config: |
      - name: StorageType
        value: "hostpath"
      - name: BasePath
        value: "/var/openebs/local"
provisioner: openebs.io/local
reclaimPolicy: Delete
volumeBindingMode: WaitForFirstConsumer
Code language: JavaScript (javascript)

Create and inspect it:

kubectl apply -f sc.yaml
kubectl get sc openebs-hostpath-lab
kubectl describe sc openebs-hostpath-lab
Code language: JavaScript (javascript)

Understanding the fields

FieldPurpose
metadata.nameName referenced in the PVC / StatefulSet.
openebs.io/cas-type: localTells OpenEBS this is a local storage class.
cas.openebs.io/configOpenEBS-specific configuration, written as an embedded YAML list.
StorageType: hostpathA directory underneath a node’s filesystem path is the backing storage.
BasePathParent directory for dynamically created PV directories.
provisioner: openebs.io/localAsks the installed OpenEBS controller to provision volumes.
reclaimPolicy: DeleteWhen a PVC and PV are removed normally, OpenEBS deletes the backing directory/data.
WaitForFirstConsumerDefers provision until the scheduler picks a node for a consuming Pod.

Do not change volumeBindingMode to Immediate. OpenEBS HostPath requires WaitForFirstConsumer because provisioning depends on the chosen node.


9. Create a StatefulSet with automatic PVCs

Save this file as sfs.yaml:

apiVersion: apps/v1
kind: StatefulSet
metadata:
  name: openebs-local-test
spec:
  serviceName: openebs-local-service
  replicas: 3
  selector:
    matchLabels:
      app: openebs-local-test
  template:
    metadata:
      labels:
        app: openebs-local-test
    spec:
      containers:
        - name: test-container
          image: busybox:1.36
          command:
            - /bin/sh
            - -c
            - sleep 100000
          volumeMounts:
            - name: local-vol
              mountPath: /usr/test-pod
  volumeClaimTemplates:
    - metadata:
        name: local-vol
        labels:
          app: openebs-local-test
      spec:
        accessModes:
          - ReadWriteOnce
        storageClassName: openebs-hostpath-lab
        resources:
          requests:
            storage: 1Gi
Code language: JavaScript (javascript)

What it creates

Kubernetes creates the PVCs from volumeClaimTemplates, and OpenEBS creates matching PVs on demand:

StatefulSet PodGenerated PVCProvisioned PV
openebs-local-test-0local-vol-openebs-local-test-0New PV created by OpenEBS
openebs-local-test-1local-vol-openebs-local-test-1New PV created by OpenEBS
openebs-local-test-2local-vol-openebs-local-test-2New PV created by OpenEBS

Each claims 1Gi, for 3Gi requested in total, not necessarily 3Gi actually consumed. Each has its own directory, and no handwritten PV or PVC manifest is required.

Why only Pod -0 may appear at first

StatefulSets default to OrderedReady. Kubernetes waits for openebs-local-test-0 to become Ready before proceeding to -1 and -2. That behavior is expected.

About the serviceName

openebs-local-service refers to the recommended headless Service for stable network identity. The storage lab can demonstrate volume creation without this Service, but create it if you need stable per-Pod DNS; see section 14.


10. Apply resources and verify dynamic provisioning

10.1 Apply both manifests

kubectl apply -f sc.yaml
kubectl apply -f sfs.yaml
Code language: CSS (css)

10.2 Watch rollout and PVC/PV binding

kubectl get pods -l app=openebs-local-test -w
Code language: JavaScript (javascript)

In another terminal:

kubectl get pvc -l app=openebs-local-test
kubectl get pv
kubectl get sc
Code language: JavaScript (javascript)

Once everything is ready, expect something like:

NAME                   READY   STATUS    RESTARTS
openebs-local-test-0   1/1     Running   0
openebs-local-test-1   1/1     Running   0
openebs-local-test-2   1/1     Running   0
NAME                             STATUS   CAPACITY   ACCESS MODES   STORAGECLASS
local-vol-openebs-local-test-0    Bound    1Gi        RWO            openebs-hostpath-lab
local-vol-openebs-local-test-1    Bound    1Gi        RWO            openebs-hostpath-lab
local-vol-openebs-local-test-2    Bound    1Gi        RWO            openebs-hostpath-lab

The PV names are automatically assigned, usually beginning with pvc-.

10.3 Inspect one PVC

kubectl describe pvc local-vol-openebs-local-test-0

Expected attributes include:

  • Status: Bound
  • StorageClass: openebs-hostpath-lab
  • Capacity: 1Gi
  • Access Modes: RWO
  • A corresponding volume name (pvc-...)

10.4 Check which nodes run your Pods

kubectl get pods -l app=openebs-local-test -o wide
Code language: JavaScript (javascript)

On your two-node cluster, there may be only one schedulable worker. In that case all three Pods can be placed on the same worker if sufficient resources are available.


11. Find the real data directory on the worker node

This is the most important verification that your dynamically provisioned volume is host-local.

11.1 Read the PV name bound to the first PVC

PV0=$(kubectl get pvc local-vol-openebs-local-test-0 \
  -o jsonpath='{.spec.volumeName}')
echo "$PV0"
Code language: PHP (php)

11.2 Inspect its local path and node affinity

kubectl get pv "$PV0" -o yaml
kubectl get pv "$PV0" -o jsonpath='{.spec.local.path}{"\n"}'
kubectl get pv "$PV0" -o jsonpath='{.spec.nodeAffinity.required.nodeSelectorTerms[0].matchExpressions[0].values[0]}{"\n"}'
Code language: PHP (php)

Expected conceptual PV fragment:

spec:
  storageClassName: openebs-hostpath-lab
  capacity:
    storage: 1Gi
  local:
    path: /var/openebs/local/pvc-example-id
  nodeAffinity:
    required:
      nodeSelectorTerms:
        - matchExpressions:
            - key: kubernetes.io/hostname
              operator: In
              values:
                - your-worker-node
Code language: JavaScript (javascript)

spec.local.path is the filesystem directory. nodeAffinity pins that PV to the node with the data.

11.3 Verify on the correct worker

Find which node runs the Pod:

kubectl get pod openebs-local-test-0 -o wide
Code language: JavaScript (javascript)

Connect to that node (SSH, SSM, console, etc.) and run:

sudo ls -lah /var/openebs/local/
df -h /var/openebs/local
Code language: JavaScript (javascript)

Do not manually delete or move the PV directory while its volume is in use. The PV path may not exist on the control-plane node or on other workers.


12. Test persistence after Pod recreation

12.1 Write a file inside the mounted PVC

kubectl exec openebs-local-test-0 -- sh -c \
  'echo "Hello from OpenEBS HostPath" > /usr/test-pod/hello.txt'
Code language: JavaScript (javascript)

12.2 Read the file

kubectl exec openebs-local-test-0 -- cat /usr/test-pod/hello.txt

Expected:

Hello from OpenEBS HostPath
Code language: JavaScript (javascript)

12.3 Delete only the Pod (not the PVC)

kubectl delete pod openebs-local-test-0
Code language: JavaScript (javascript)

The StatefulSet controller recreates it. Check readiness:

kubectl rollout status statefulset/openebs-local-test --timeout=180s
kubectl get pods -l app=openebs-local-test -o wide
Code language: JavaScript (javascript)

12.4 Read the original file after restart

kubectl exec openebs-local-test-0 -- cat /usr/test-pod/hello.txt

Expected:

Hello from OpenEBS HostPath
Code language: JavaScript (javascript)

What this proves: the file survives Pod replacement because the PVC remains bound to the existing PV. Kubernetes schedules the replacement to the node containing the local volume. It does not prove recovery from node/disk loss.


13. Test storage isolation across replicas

Each Pod receives its own PVC; writing to one volume must not write to the others.

13.1 Write a unique value in each Pod

for i in 0 1 2; do
  kubectl exec "openebs-local-test-$i" -- sh -c \
    "echo data-from-pod-$i > /usr/test-pod/identity.txt"
done
Code language: JavaScript (javascript)

13.2 Read from each Pod

for i in 0 1 2; do
  echo "Pod $i:"
  kubectl exec "openebs-local-test-$i" -- \
    cat /usr/test-pod/identity.txt
done
Code language: PHP (php)

Expected:

Pod 0:
data-from-pod-0
Pod 1:
data-from-pod-1
Pod 2:
data-from-pod-2
Code language: JavaScript (javascript)

This demonstrates one claim and one separate storage directory per replica.


14. Optional: Headless Service for stable DNS

The lab’s storage creation does not depend on a Service. However, a StatefulSet normally uses a headless Service to provide stable Pod network identities.

If you want full StatefulSet DNS behavior, create service.yaml:

apiVersion: v1
kind: Service
metadata:
  name: openebs-local-service
spec:
  clusterIP: None
  selector:
    app: openebs-local-test
  ports:
    - name: http
      port: 80
      targetPort: 80

Apply:

kubectl apply -f service.yaml
kubectl get svc openebs-local-service
Code language: JavaScript (javascript)

This sample Service’s port is for DNS/identity demonstration only. The BusyBox container does not listen on port 80; no HTTP request will succeed until you run a server. If your only aim is testing storage and you insist on just two manifest types, skip this optional Service.


15. Advanced configuration

15.1 Change the host storage directory

For a dedicated mounted data disk, change the BasePath in a new StorageClass:

apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
  name: openebs-hostpath-data-disk
  annotations:
    openebs.io/cas-type: local
    cas.openebs.io/config: |
      - name: StorageType
        value: "hostpath"
      - name: BasePath
        value: "/mnt/openebs-data"
provisioner: openebs.io/local
reclaimPolicy: Delete
volumeBindingMode: WaitForFirstConsumer
Code language: JavaScript (javascript)

Make sure /mnt/openebs-data is a real mounted filesystem on any node eligible for these workloads. Provisioning to an unmounted directory can unexpectedly fill the OS disk.

15.2 Restrict provisioning to specified nodes

Use allowedTopologies in the StorageClass to permit only particular worker hostnames:

allowedTopologies:
  - matchLabelExpressions:
      - key: kubernetes.io/hostname
        values:
          - YOUR-WORKER-NODE-NAME

Insert this at the StorageClass top level alongside volumeBindingMode; replace the hostname. This influences both provisioning and scheduling. Avoid hard-coding Pod nodeName when using WaitForFirstConsumer, because bypassing the scheduler can leave the claim Pending.

15.3 Set directory permissions

OpenEBS HostPath’s default newly created directory mode is usually 0777. For tighter access, add a FilePermissions entry to the cas.openebs.io/config annotation:

      - name: FilePermissions
        data:
          mode: "0770"

For example, a full custom StorageClass with permissions:

apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
  name: openebs-hostpath-restricted
  annotations:
    openebs.io/cas-type: local
    cas.openebs.io/config: |
      - name: StorageType
        value: "hostpath"
      - name: BasePath
        value: "/var/openebs/restricted"
      - name: FilePermissions
        data:
          mode: "0770"
provisioner: openebs.io/local
reclaimPolicy: Delete
volumeBindingMode: WaitForFirstConsumer
Code language: JavaScript (javascript)

Confirm your application UID/GID can access the volume before tightening permissions. These settings affect new directories, not existing ones.

15.4 Set Retain instead of Delete

Use:

reclaimPolicy: Retain
Code language: HTTP (http)

With Retain, Kubernetes does not automatically remove the volume’s data when its PVC is deleted. This protects against automatic cleanup but also requires manual recovery and management of the retained volume. Retain is not a backup strategy.

15.5 Optional quotas (advanced, not needed for the lab)

By default, storage: 1Gi is not a filesystem quota. OpenEBS 4.6 documents optional XFSQuota and EXT4Quota, but these require correct filesystem features and project quota mount configuration. Do not copy quota settings into the beginner lab without configuring the node filesystem first.

Official guides:

15.6 Install from a consolidated Helm values file (optional)

For repeatable configuration, create openebs-values.yaml:

engines:
  local:
    lvm:
      enabled: false
    zfs:
      enabled: false
    rawfile:
      enabled: false
  replicated:
    mayastor:
      enabled: false
loki:
  enabled: false
alloy:
  enabled: false
Code language: JavaScript (javascript)

Then use this instead of the --set form in step 6:

helm upgrade --install openebs openebs/openebs \
  -n openebs --create-namespace \
  --version 4.6.2 \
  -f openebs-values.yaml

Validate chart keys with helm show values before applying future chart releases.


16. Troubleshooting common errors

16.1 PVC stuck in Pending: WaitForFirstConsumer

Check:

kubectl describe pvc local-vol-openebs-local-test-0
kubectl describe pod openebs-local-test-0
kubectl get pods -n openebs
Code language: JavaScript (javascript)

A claim can show WaitForFirstConsumer briefly. It is not automatically a failure. Look for the scheduler’s error event and ensure a Pod consumes the PVC.

If the Pod shows FailedScheduling, continue below.

16.2 0/2 nodes are available ... untolerated taint(s)

Your earlier cluster logs had this pattern. Check:

kubectl get nodes -o wide
kubectl get nodes -o custom-columns=NAME:.metadata.name,TAINTS:.spec.taints
kubectl describe pod openebs-local-test-0
Code language: JavaScript (javascript)

A control-plane taint is normal. Make sure at least one Ready worker is available. Do not remove a control-plane taint simply to get this lab running unless it is an intentionally single-node training cluster and you understand the implications.

16.3 PVC Pending because provisioner is missing or broken

kubectl get pods -n openebs -o wide
kubectl get deployments -n openebs
kubectl logs -n openebs deployment/openebs-localpv-provisioner --tail=150
kubectl describe pvc local-vol-openebs-local-test-0
Code language: JavaScript (javascript)

Check for image pull issues, CRD/RBAC failures, unhealthy controller Pods, or incorrect StorageClass provisioner values.

16.4 Wrong StorageClass

kubectl get sc
kubectl get pvc local-vol-openebs-local-test-0 -o yaml
Code language: JavaScript (javascript)

For this tutorial the claim’s storageClassName must be openebs-hostpath-lab and that StorageClass’s provisioner must be openebs.io/local.

If the claim still requests your old local-storage (kubernetes.io/no-provisioner) class, you are running the old manifest or an old StatefulSet/PVC. For a disposable lab, recreate those resources after confirming there is no data to preserve. Do not delete persistent claims containing valuable data.

16.5 Immediate mode prevents local provisioning

OpenEBS HostPath needs:

volumeBindingMode: WaitForFirstConsumer
Code language: HTTP (http)

If you created a StorageClass with Immediate, create a corrected one with a new name and update a newly created application/PVC to use it. StorageClass binding/provisioner behavior cannot be reliably changed in place.

16.6 Using nodeName with WaitForFirstConsumer

Do not set:

spec:
  template:
    spec:
      nodeName: some-worker

This bypasses normal scheduler selection and can prevent local provisioning. Use a nodeSelector, node affinity, or the StorageClass’s allowedTopologies if placement restrictions are needed.

16.7 Permission denied in the mounted path

kubectl exec openebs-local-test-0 -- sh -c \
  'id; ls -ld /usr/test-pod; touch /usr/test-pod/write-test'
Code language: JavaScript (javascript)

Check the volume directory mode and the container’s UID/GID. With a non-root workload, set appropriate file permissions / fsGroup where supported, rather than blindly making all directories world-writable.

16.8 Pod stays Pending after PV is Bound

kubectl describe pod openebs-local-test-0
kubectl get pv "$PV0" -o yaml
kubectl get nodes -o wide
Code language: JavaScript (javascript)

A local PV has nodeAffinity. The Pod must be able to run on that specific node. Node unavailability, taints, or conflicting node selectors can block it. Data does not automatically move to another node.

16.9 Pod ImagePullBackOff

kubectl describe pod openebs-local-test-0
kubectl get events --sort-by=.lastTimestamp | tail -30
Code language: JavaScript (javascript)

The test image is busybox:1.36, not the retired k8s.gcr.io/busybox reference. Verify node egress to the image registry or use an approved mirror.

16.10 Disk-full or capacity concerns

On the actual worker:

df -h /var/openebs/local
sudo du -sh /var/openebs/local/* 2>/dev/null
Code language: JavaScript (javascript)

OpenEBS HostPath is directory-backed. Without project quotas, applications can use more than the PVC request and may fill the node’s filesystem. Plan free space and disk-pressure alerts.

16.11 Directory exists but data appears missing

Check Pod-to-node placement, the PV’s spec.local.path, and the mount inside the Pod. Do not assume all cluster nodes have identical data at /var/openebs/local.

kubectl get pod openebs-local-test-0 -o wide
kubectl get pvc local-vol-openebs-local-test-0
kubectl get pv "$PV0" -o jsonpath='{.spec.local.path}{"\n"}'
Code language: PHP (php)

16.12 Common issue checklist

kubectl get nodes -o wide
kubectl get sc
kubectl get pods -n openebs -o wide
kubectl get sts,pods,pvc -l app=openebs-local-test
kubectl get pvc local-vol-openebs-local-test-0 -o yaml
kubectl describe pod openebs-local-test-0
kubectl get events -A --sort-by=.lastTimestamp | tail -40
helm status openebs -n openebs
Code language: JavaScript (javascript)

17. Storage lifecycle, retention, and cleanup

17.1 Understand what gets deleted

Deleting a StatefulSet does not normally delete its PVCs. The PVCs may remain to preserve data. Deleting a PVC backed by a PV with reclaimPolicy: Delete generally triggers deletion of that PV and its backing directory. The exact cleanup depends on the controller operating correctly and access to the node where the directory resides.

WARNING — destructive: The cleanup commands below intentionally remove the test PVCs and their stored data. Only run them on disposable lab resources and after backing up anything important.

17.2 Remove the application first

kubectl delete statefulset openebs-local-test
kubectl get pvc -l app=openebs-local-test
Code language: JavaScript (javascript)

17.3 Remove only this lab’s claims

kubectl delete pvc \
  local-vol-openebs-local-test-0 \
  local-vol-openebs-local-test-1 \
  local-vol-openebs-local-test-2
Code language: JavaScript (javascript)

Confirm the corresponding PVs are gone:

kubectl get pv
Code language: JavaScript (javascript)

Do not delete unrelated manually provisioned PVs such as your earlier hostpath, hostpath2, or deehostpath volumes.

17.4 Delete the custom StorageClass

kubectl delete storageclass openebs-hostpath-lab
Code language: JavaScript (javascript)

If you created the optional headless Service:

kubectl delete service openebs-local-service
Code language: JavaScript (javascript)

17.5 Optionally uninstall OpenEBS (ONLY if no workloads use it)

helm uninstall openebs -n openebs

Before uninstalling, check whether any PVCs/PVs across the cluster still use openebs.io/local. Do not uninstall a provisioner while important PVs rely on it, because reclaim and operational actions can fail afterward.

kubectl get sc
kubectl get pv -o custom-columns=NAME:.metadata.name,SC:.spec.storageClassName,STATUS:.status.phase
kubectl get pvc -A
Code language: JavaScript (javascript)

Namespace deletion and CRD removal may affect other OpenEBS workloads and should not be treated as routine lab cleanup.


18. Operational considerations and limitations

ConcernOpenEBS Local PV HostPath behavior
Dynamic PV/PVC provisioningYes.
Cloud provider neededNo.
Dedicated raw block device neededNo; directory on an existing mounted filesystem is sufficient.
Data available from all nodesNo, data lives on one node.
Transparent node-failure recoveryNo. A failed/lost node means the local volume is inaccessible.
ReplicationNo.
Access modeReadWriteOnce; not a shared ReadWriteMany filesystem.
SchedulingUses WaitForFirstConsumer and local PV node affinity.
Hard 1Gi quota by defaultNo. Optional project quotas require explicit filesystem configuration.
BackupsYour responsibility (e.g. application-aware backups or compatible backup tooling).
Common use casesLearning, CI labs, caches, disposable development databases, single-node/local workloads.
Production suitabilityOnly if you explicitly accept node-local failure characteristics and implement backups and monitoring.

Security: directory-backed node volumes should be considered powerful local storage access. Restrict who may create StorageClasses, Pods with host mounts, and privileged workloads. Avoid storing sensitive data without appropriate host filesystem permissions and backup controls.

18.1 When not to use OpenEBS Local PV HostPath

For a multi-node database requiring automatic failover with replicated storage, use a storage system designed for replication, such as Longhorn or OpenEBS Mayastor (subject to prerequisites), rather than expecting local hostpath volumes to follow Pods between nodes.

18.2 1Gi is a request, not a reservation guarantee

The requested capacity shows in Kubernetes API objects, but the backing filesystem is shared with other directories. Monitor node free space; do not infer that three 1Gi PVCs are physically isolated 1Gi partitions.


19. Frequently asked questions

Q1. Can kubernetes.io/no-provisioner dynamically create local PVs?
No. It explicitly means no dynamic provisioner. Use OpenEBS or another installed dynamic provisioner.

Q2. Do I need to create PersistentVolumes manually?
No. Once OpenEBS is running and a suitable PVC is requested, it creates the PV dynamically.

Q3. Do I need a standalone PVC YAML?
No, because volumeClaimTemplates in the StatefulSet automatically creates one PVC per Pod replica.

Q4. Can I use only StorageClass + StatefulSet YAML?
Yes for the application storage demonstration. OpenEBS itself must already be installed, and the optional headless Service is for StatefulSet DNS, not for volume provisioning.

Q5. Does openebs.io/local mean Kubernetes PV spec.hostPath?
No. OpenEBS HostPath normally uses spec.local.path with node affinity; the actual data is stored in a host filesystem directory.

Q6. What happens if I delete a Pod?
The StatefulSet recreates it and reuses the existing PVC/PV. Data should persist if the same underlying node/disk remains available.

Q7. What happens if the worker node disappears permanently?
The volume is not automatically replicated or migrated. Recovery depends on restoring that node/disk or recovering from backups.

Q8. Can three replicas share the same PVC?
Not in this design. volumeClaimTemplates gives each replica its own PVC, which is the right model for independent local storage.

Q9. Can I use my old local-storage StorageClass name?
Only after planning migration/removal of the existing StorageClass with kubernetes.io/no-provisioner and any dependent resources. The provisioner field is immutable. This tutorial uses openebs-hostpath-lab so you do not disturb existing workloads.

Q10. Is 1Gi strictly enforced?
Not by default. OpenEBS documents optional filesystem project quotas for supported XFS/ext4 setups.

Q11. Do I need to untaint the control plane?
Usually no. An ordinary worker node is sufficient for the example.

Q12. Does the default openebs-hostpath StorageClass already work?
Yes, after a successful OpenEBS HostPath installation. Your custom class is useful for learning and customizing the base path.


20. Command cheat sheet

<em># PRECHECK</em>
kubectl config current-context
kubectl get nodes -o wide
kubectl get sc
kubectl get pvc -A
helm version

<em># HELM REPOSITORY</em>
helm repo add openebs https://openebs.github.io/openebs
helm repo update

<em># HOSTPATH-ONLY INSTALL</em>
helm upgrade --install openebs openebs/openebs \
  -n openebs --create-namespace --version 4.6.2 \
  --set engines.replicated.mayastor.enabled=false \
  --set engines.local.lvm.enabled=false \
  --set engines.local.zfs.enabled=false \
  --set engines.local.rawfile.enabled=false \
  --set loki.enabled=false \
  --set alloy.enabled=false

<em># VERIFY PROVISIONER</em>
helm status openebs -n openebs
kubectl get pods -n openebs
kubectl get sc openebs-hostpath

<em># CREATE LAB SC + STATEFULSET</em>
kubectl apply -f sc.yaml
kubectl apply -f sfs.yaml

<em># CHECK DYNAMIC STORAGE</em>
kubectl get pods -l app=openebs-local-test -o wide
kubectl get pvc -l app=openebs-local-test
kubectl get pv

<em># FIND HOST-LOCAL DIRECTORY</em>
PV0=$(kubectl get pvc local-vol-openebs-local-test-0 -o jsonpath='{.spec.volumeName}')
kubectl get pv "$PV0" -o jsonpath='{.spec.local.path}{"\n"}'
kubectl get pv "$PV0" -o yaml

<em># PERSISTENCE TEST</em>
kubectl exec openebs-local-test-0 -- sh -c \
  'echo "Hello from OpenEBS" > /usr/test-pod/hello.txt'
kubectl delete pod openebs-local-test-0
kubectl rollout status statefulset/openebs-local-test --timeout=180s
kubectl exec openebs-local-test-0 -- cat /usr/test-pod/hello.txt

<em># TROUBLESHOOT</em>
kubectl describe pod openebs-local-test-0
kubectl describe pvc local-vol-openebs-local-test-0
kubectl logs -n openebs deployment/openebs-localpv-provisioner --tail=150

<em># CLEANUP (DELETES THIS LAB'S DATA)</em>
kubectl delete statefulset openebs-local-test
kubectl delete pvc local-vol-openebs-local-test-{0,1,2}
kubectl delete sc openebs-hostpath-lab
Code language: HTML, XML (xml)

21. Official references

The examples and behaviors were checked against these primary sources:

  1. OpenEBS 4.6 installation guide — https://openebs.io/docs/quickstart-guide/installation
  2. OpenEBS Local PV HostPath overview — https://openebs.io/docs/user-guides/localpv-hostpath
  3. Create HostPath StorageClasses — https://openebs.io/docs/user-guides/local-storage-user-guide/local-pv-hostpath/configuration/hostpath-create-storageclass
  4. HostPath StorageClass parameters — https://openebs.io/docs/main/user-guides/local-storage-user-guide/local-pv-hostpath/configuration/hostpath-storageclass-parameters
  5. HostPath deployment example — https://openebs.io/docs/quickstart-guide/deployment
  6. Local storage troubleshooting — https://openebs.io/docs/troubleshooting
  7. OpenEBS releases — https://github.com/openebs/openebs/releases
  8. Official OpenEBS Helm chart repository — https://openebs.github.io/openebs/
  9. Helm installation documentation — https://helm.sh/docs/intro/install/

Tutorial result: On a self-managed Ubuntu Kubernetes cluster, OpenEBS openebs.io/local dynamically creates separate host-local PersistentVolumes for StatefulSet PVCs. Your application configuration requires only a StorageClass and a StatefulSet (plus an optional headless Service for normal StatefulSet DNS), while the OpenEBS controller must be installed and healthy in the cluster.

Find Trusted Cardiac Hospitals

Compare heart hospitals by city and services — all in one place.

Explore Hospitals
I'm Rajesh Kumar, a DevOps, SRE, DevSecOps, Cloud, and Platform Engineering expert passionate about sharing practical knowledge, real-world experiences, and industry best practices. I have worked at Cotocus and regularly write about technology, travel, investing, health, product reviews, and digital marketing through my various platforms. I publish technical articles at DevOps School, travel stories at Holiday Landmark, stock market insights at Stocks Mantra, health and fitness guidance at My Medic Plus, product reviews at TrueReviewNow, and SEO and digital marketing strategies at Wizbrand.

Related Posts

Kubernetes storageclass using Rancher Local Path Provisioner: Complete Step-by-Step Kubernetes Tutorial

Goal: Automatically provision hostPath-backed PersistentVolumes (PVs) for a 3-replica StatefulSet on a self-managed Ubuntu Kubernetes cluster, without hand-writing a PV or a PVC manifest. Lab setting Value Kubernetes v1.35.10 (self-managed / kubeadm-style installation)…

Read More

Understanding the Hardware Requirements of Modern LLMs

Ask ten engineers how much hardware a large language model needs, and you will get ten answers, most of them starting with “it depends.” That answer is…

Read More

Best Remote SDR Outsourcing Companies 2026

Quick Answer The best remote SDR outsourcing companies in 2026 include Remote Growth Partners, Virtustant, Hire Overseas, CIENCE, SalesRoads, Belkins, SalesHive, Leadium, Martal Group, and Callbox. Dedicated…

Read More

Top Legacy Software Modernization Companies in Europe: A Full Guide for 2027

The UK government’s State of Digital Government Review put legacy systems at 28% of central government department estates in 2024, up from 26% a year earlier. Inside…

Read More

Using Incentives: Get Users Willing to Help Your Business

Getting users excited about an activity they don’t value or enjoy is tough. In this case, a reward system can do wonders, whether it’s something minor or…

Read More

What It Takes to Build an Online Bingo Site

Starting an online bingo site takes quite a bit of planning. You need a platform, access to bingo games, payment services, a way to reach players and…

Read More
Subscribe
Notify of
guest
0 Comments
Newest
Oldest Most Voted
0
Would love your thoughts, please comment.x
()
x