Minikube’s Persistent Volumes (PVs) and Persistent Volume Claims (PVCs) are often misunderstood as direct mappings to a local directory, but they’re actually abstractions that rely on a storage provisioner, even in a local development environment.
Let’s see this in action. First, we need a simple application that needs persistent storage. Here’s a basic Nginx deployment that will write its access logs to a persistent volume:
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: nginx-logs-pvc
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 1Gi
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-logs
spec:
replicas: 1
selector:
matchLabels:
app: nginx-logs
template:
metadata:
labels:
app: nginx-logs
spec:
containers:
- name: nginx
image: nginx:latest
ports:
- containerPort: 80
volumeMounts:
- name: log-volume
mountPath: /var/log/nginx
volumes:
- name: log-volume
persistentVolumeClaim:
claimName: nginx-logs-pvc
Apply this to your Minikube cluster:
kubectl apply -f pvc-and-deployment.yaml
Now, let’s inspect the created PVC and the Pod:
kubectl get pvc nginx-logs-pvc
kubectl get pod -l app=nginx-logs -o wide
You’ll see the PVC is Bound, and the Pod is Running. If you exec into the pod and check the /var/log/nginx directory, you’ll find the access logs.
The magic here isn’t that Minikube automatically creates a directory for you. It’s that Minikube includes a default storage provisioner, typically kubernetes.io/no-provisioner or a dynamic provisioner like minikube-hostpath (depending on your Minikube version and driver). When you create a PVC with spec.resources.requests.storage, this provisioner watches for unbound PVCs and dynamically creates a corresponding PersistentVolume object. For minikube-hostpath, it creates a PV that points to a specific directory on your Minikube VM’s filesystem.
The problem this solves is decoupling application storage requirements from the underlying infrastructure. An application developer defines a PersistentVolumeClaim specifying the type of storage needed (e.g., ReadWriteOnce, 1Gi). The cluster administrator (or in Minikube’s case, the provisioner) then ensures a PersistentVolume matching that claim is available. This allows you to move your application definition between different Kubernetes environments (local, staging, production) without changing the storage declarations.
Internally, when you use the hostpath provisioner (which is common for minikube), the process looks like this:
- You create a
PersistentVolumeClaim(e.g.,nginx-logs-pvc). - The
minikube-hostpathprovisioner, running as a controller in your cluster, detects this unbound PVC. - It creates a
PersistentVolumeobject. This PV will have ahostPathfield pointing to a directory within the Minikube VM’s filesystem, like/mnt/data/my-pv-name. - The provisioner then binds the PVC to this newly created PV.
- When your Pod starts and references the PVC, Kubernetes mounts the
hostPathdirectory from the PV into your container at the specifiedmountPath.
The accessModes are crucial. ReadWriteOnce (RWO) means the volume can be mounted as read-write by a single node. In Minikube’s hostpath setup, this is generally fine because Minikube runs on a single node. ReadOnlyMany (ROX) allows multiple nodes to mount it read-only, and ReadWriteMany (RWX) allows multiple nodes to mount it read-write. For hostpath on Minikube, only RWO is reliably supported. For RWX, you’d typically need network-attached storage solutions like NFS or distributed file systems.
The storageClassName field on the PVC is key to selecting which provisioner creates the PV. If you don’t specify one, Kubernetes uses the default StorageClass. Minikube often has a default StorageClass configured. You can check it with kubectl get storageclass. If you wanted to be explicit, your PVC would look like this:
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: nginx-logs-pvc
spec:
accessModes:
- ReadWriteOnce
storageClassName: standard # Or whatever your default is
resources:
requests:
storage: 1Gi
A common pitfall is expecting the hostPath to be directly on your host machine’s filesystem. It’s actually within the Minikube VM’s filesystem, which is why you can’t directly access the /var/log/nginx data from your host’s terminal without extra steps (like minikube ssh and then navigating to the path). The actual path on the Minikube VM might look something like /var/lib/minikube/data/my-volume-name.
Once your PVC is bound and your Pod is running, the next step is usually to ensure data survives Pod restarts or deletions by understanding how to manage the lifecycle of these dynamically provisioned volumes, especially when you want to reuse them or clean them up.