Other volume types
Most workloads need one of two things: scratch space that belongs to the Pod, or a claim that a StorageClass turns into a real disk. Between and around those sit the other volume types — and knowing which ones a hardened cluster will even accept saves a lot of time.
This page is the map. It is deliberately more reference than tutorial: you do not need all of these, but you need to recognise them and know which to reach for.
Reference
- https://kubernetes.io/docs/concepts/storage/volumes/
- https://kubernetes.io/docs/concepts/security/pod-security-standards/
The Types You Will Meet
| Type | Lives where | Use it when |
|---|---|---|
emptyDir | Node, Pod lifetime | Scratch space, sharing between containers (previous page) |
configMap, secret, downwardAPI, projected | API server, injected | Configuration and identity as files |
persistentVolumeClaim | Wherever the driver puts it | Anything that must outlive the Pod (next page) |
hostPath | A directory on the node | Almost never. See below |
local | A disk on one node, via a PV | High-performance node-local storage where you control scheduling |
nfs, Azure Files | A shared filesystem | ReadWriteMany, shared between Pods on different nodes |
CSI (csi) | Whatever the driver manages | Everything modern: cloud disks, snapshots, encryption |
ephemeral | A PVC created per Pod | A real disk with the Pod’s lifetime |
hostPath Is Usually The Wrong Answer
hostPath mounts a directory from the node’s filesystem into the container. It is the classic shortcut in tutorials, and on any hardened cluster it will not even be admitted. This is what the API server says:
Error from server (Forbidden): pods "x" is forbidden:
violates PodSecurity "baseline:latest": hostPath volumes (volume "host")That is baseline — the middle level — not restricted. Both reject it, and for good reason:
- The Pod can read and write the node’s filesystem, which is a route to compromising the node and everything on it.
- The data is tied to one node, so the Pod cannot be rescheduled without silently getting a different (or empty) directory.
- Two Pods on the same node can collide in a directory neither of them owns.
There are legitimate uses — a node agent that genuinely must read /var/log — and they come with an explicit exemption on a dedicated namespace, not with a blanket relaxation. If you find yourself reaching for hostPath, the usual right answers are an emptyDir for scratch, a PVC for data, or a DaemonSet with a deliberate policy exception.
Note the consequence for lab exercises you may find elsewhere: a “create a manual PersistentVolume with hostPath” walkthrough cannot run in a namespace enforcing baseline or restricted. This course uses the real storage classes instead.
local Volumes: Node-Pinned, But Legitimate
A local volume is a PersistentVolume backed by a disk attached to one node, with a nodeAffinity that pins it there. It differs from hostPath in that it is a real PV — administratively created, with a lifecycle, capacity and reclaim policy — rather than an arbitrary directory a Pod can grab.
You get the performance of directly attached storage, and you pay for it in flexibility: a Pod using a local volume can only ever run on that one node. If the node dies, the data is unavailable until it comes back. This is the storage behind high-performance systems such as distributed databases on dedicated node pools.
Shared Storage: NFS and Azure Files
ReadWriteMany needs a filesystem many nodes can mount at once, so it cannot be a disk attached to one node. In practice that means NFS or a managed equivalent — on this platform, Azure Files.
The trade is speed for sharing: a shared filesystem is usually slower than an attached disk, and its consistency semantics are those of the filesystem. Use it for content that genuinely needs to be mounted in several places at once, not as a default.
CSI: How Storage Actually Plugs In
The Container Storage Interface is the mechanism by which storage vendors plug into Kubernetes. A driver runs as Pods in the cluster, registers itself, and implements provisioning, attaching and mounting. The name you see in a StorageClass is its provisioner:
kubectl get storageclass
kubectl get storageclass standard -o jsonpath='{.provisioner}{"\n"}'
kubectl get csidrivers
kubectl get csinodesRun those against your own cluster and against the shared cluster, and compare. In this cluster you will see no CSI drivers at all — the default class uses rancher.io/local-path, a simple external provisioner that hands out directories on the node. That single fact explains two limitations you have already met:
ALLOWVOLUMEEXPANSIONisfalse, because local-path cannot grow a directory.- There are no volume snapshots, because snapshots are a CSI feature.
On the shared cluster you will find Azure drivers — disk.csi.azure.com and file.csi.azure.com — and their StorageClasses behave differently: expansion is supported, snapshots exist, and the volumes are real managed disks. This is the cleanest illustration in the course of a point worth remembering: Kubernetes defines the storage API; the driver defines what is actually possible. Two clusters running the same Kubernetes version can have entirely different storage capabilities.
Exercises
Inventory the storage in front of you.
kubectl get storageclass -o custom-columns=\ NAME:.metadata.name,PROVISIONER:.provisioner,BINDING:.volumeBindingMode,EXPAND:.allowVolumeExpansion,DEFAULT:.metadata.annotations kubectl get csidrivers kubectl get pvkubectl get pvshows the volumes your claims produced. Look at one in full and find the driver, the handle, and any node affinity:kubectl get pv <name> -o yaml | head -40See
hostPathrefused, with the reason. In a namespace with no policy,hostPathis admitted. In one labelledpod-security.kubernetes.io/enforce: baseline, it is not:kubectl create namespace ps-demo kubectl label namespace ps-demo pod-security.kubernetes.io/enforce=baseline kubectl -n ps-demo apply -f hostpath-pod.yaml # read the errorThen try the same in a namespace labelled
warninstead ofenforce, and notice that the Pod is created with a warning. That is how you roll these standards out to an existing cluster without breaking everything at once.Compare two clusters. Against the shared cluster and against your own, run:
kubectl get storageclass kubectl get csidriversNote which classes are
ReadWriteOnceand which areReadWriteMany, which support expansion, and which have snapshots available. That comparison is the skill: “can this cluster do what I need?” is answered by these two commands, not by the Kubernetes version.Read a StorageClass properly.
kubectl get storageclass azurefile-csi -o yaml(on the shared cluster) and findprovisioner,reclaimPolicy,volumeBindingMode,allowVolumeExpansionandmountOptions. Every one of those changes what a claim against it will do.
Gotchas Worth Knowing
- A StorageClass whose provisioner nothing implements leaves claims
Pendingforever, with no error on the claim itself. Check that the driver is actually running. - Node-pinned volumes make scheduling part of storage. A Pod with a
localvolume or an Azure Disk in one zone can only run in one place. - RWX is a property of the storage, not a setting you can switch on. If the class does not support it, the claim stays
Pending. - A
PersistentVolumeis cluster-scoped; aPersistentVolumeClaimis namespaced. That asymmetry is why an admin can pre-provision storage for a namespace, and why a claim cannot be moved between namespaces. - Do not create PVs by hand unless you are building something deliberately. Dynamic provisioning exists so that you do not have to, and hand-built volumes are the usual home of stale data and reclaim surprises.
What to Take Away
- Reach for
emptyDirfor scratch, a PVC for anything durable, and a shared filesystem only when you genuinely need many-writers. hostPathis refused bybaselineandrestricted, and reaching for it usually signals a design problem rather than a storage requirement.- Kubernetes defines the storage API; the CSI driver defines what is possible. Check the driver, not the version.
- The two commands that answer “what storage does this cluster have?” are
get storageclassandget csidrivers.