Kubernetes Persistent Volume access modes define how a volume can be mounted by nodes in a cluster, governing concurrency and read/write behavior.
Table of Contents#
- Overview
- Access Mode Definitions
- CSI Driver Backend Support
- StorageClass Examples
- StatefulSet vs Deployment Considerations
- Performance Implications
- Choosing the Right Access Mode
- Troubleshooting
- See Also
- Sources
1. Overview#
Persistent Volumes (PVs) in Kubernetes abstract the underlying storage implementation and expose it to pods via Persistent Volume Claims (PVCs). Each PV declares which access modes it supports, and each PVC requests a specific access mode. The access mode determines whether the volume can be mounted by a single node or multiple nodes, and whether writes are permitted.
Access modes are enforced at the node level, not the pod level. A volume with ReadWriteOnce can be mounted by multiple pods, provided they all run on the same node.
2. Access Mode Definitions#
ReadWriteOnce (RWO)#
- Abbreviation:
ReadWriteOnce - Behavior: The volume can be mounted as read-write by a single node
- Multiple pods: Yes, if all pods are on the same node
- Use cases: Single-instance databases (PostgreSQL, MySQL), application state directories, CI build caches
ReadOnlyMany (ROX)#
- Abbreviation:
ReadOnlyMany - Behavior: The volume can be mounted as read-only by many nodes simultaneously
- Multiple pods: Yes, across multiple nodes, all read-only
- Use cases: Shared configuration data, static web content, pre-built machine learning models, shared certificate stores
ReadWriteMany (RWX)#
- Abbreviation:
ReadWriteMany - Behavior: The volume can be mounted as read-write by many nodes simultaneously
- Multiple pods: Yes, across multiple nodes, all read-write
- Use cases: Shared upload directories, CMS media storage, shared build artifacts, collaborative application data
ReadWriteOncePod (RWOP)#
- Abbreviation:
ReadWriteOncePod(Kubernetes 1.27+ GA) - Behavior: The volume can be mounted as read-write by a single pod on a single node
- Multiple pods: No, strictly one pod cluster-wide
- Use cases: Databases requiring exclusive access guarantees, leader-election storage, write-ahead logs
3. CSI Driver Backend Support#
Not all storage backends support all access modes. The table below maps common CSI drivers to their supported modes:
| Storage Backend | RWO | ROX | RWX | RWOP | Notes |
|---|---|---|---|---|---|
| Longhorn | Yes | Yes | Yes | Yes | RWX via NFS share manager; performance reduced |
| NFS (nfs-subdir-external-provisioner) | Yes | Yes | Yes | No | All modes via NFS protocol; no block support |
| Ceph RBD (rbd.csi.ceph.com) | Yes | Yes | No | Yes | Block device; ROX via read-only clone |
| CephFS (cephfs.csi.ceph.com) | Yes | Yes | Yes | No | Filesystem-level sharing |
| AWS EBS (ebs.csi.aws.com) | Yes | No | No | Yes | Block device, single-attach only |
| GCE PD (pd.csi.storage.gke.io) | Yes | Yes | No | Yes | ROX via multi-attach read-only |
| Azure Disk (disk.csi.azure.com) | Yes | No | No | Yes | Block device, single-attach only |
| Azure Files (file.csi.azure.com) | Yes | Yes | Yes | No | SMB/NFS-based |
| iSCSI (generic) | Yes | No | No | No | Block device, single-initiator |
| Local PV | Yes | No | No | Yes | Node-local, no network sharing |
| OpenEBS (Jiva/cStor) | Yes | No | No | No | Block replication |
| Portworx | Yes | Yes | Yes | Yes | Full-featured SDS |
4. StorageClass Examples#
Longhorn (RWO, Default)#
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
name: longhorn
provisioner: driver.longhorn.io
parameters:
numberOfReplicas: "3"
staleReplicaTimeout: "2880"
dataLocality: "best-effort"
reclaimPolicy: Delete
volumeBindingMode: Immediate
allowVolumeExpansion: trueLonghorn RWX#
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
name: longhorn-rwx
provisioner: driver.longhorn.io
parameters:
numberOfReplicas: "3"
nfsOptions: "vers=4.1,hard,timeo=600"
reclaimPolicy: Delete
volumeBindingMode: Immediate
allowVolumeExpansion: trueNFS#
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
name: nfs
provisioner: nfs.csi.k8s.io
parameters:
server: <nfs-server-ip>
share: /srv/nfs/k8s
reclaimPolicy: Delete
volumeBindingMode: Immediate
mountOptions:
- nfsvers=4.1
- hard
- noatimeCeph RBD#
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
name: ceph-rbd
provisioner: rbd.csi.ceph.com
parameters:
clusterID: <ceph-cluster-id>
pool: kubernetes
imageFeatures: layering
csi.storage.k8s.io/provisioner-secret-name: csi-rbd-secret
csi.storage.k8s.io/provisioner-secret-namespace: ceph-system
csi.storage.k8s.io/node-stage-secret-name: csi-rbd-secret
csi.storage.k8s.io/node-stage-secret-namespace: ceph-system
reclaimPolicy: Delete
allowVolumeExpansion: truePVC Requesting a Specific Access Mode#
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: my-pvc
spec:
accessModes:
- ReadWriteOnce
storageClassName: longhorn
resources:
requests:
storage: 10Gi5. StatefulSet vs Deployment Considerations#
| Aspect | Deployment + PVC | StatefulSet + volumeClaimTemplates |
|---|---|---|
| PVC binding | All pods share one PVC | Each pod gets its own PVC (e.g., data-myapp-0, data-myapp-1) |
| Access mode required | RWX (if replicas > 1 across nodes) or RWO (if single replica or co-located) | RWO is sufficient (each pod has its own volume) |
| Scaling | New replicas mount the same volume | New replicas get new PVCs automatically |
| Data isolation | No isolation between replicas | Full isolation; each replica has its own data |
| Storage backend | Must support RWX for multi-node | Any backend supporting RWO works |
| Pod identity | Pods are interchangeable | Pods have stable identities (ordinal index) |
| Deletion | PVC persists; manually delete | PVCs persist by default; use persistentVolumeClaimRetentionPolicy (1.27+) |
When to Use Each#
- StatefulSet with RWO: Databases (PostgreSQL, MySQL, MongoDB), message brokers (Kafka, RabbitMQ), any workload needing per-instance persistent state
- Deployment with RWX: Shared media uploads, CMS content, log aggregation directories, shared build caches
- Deployment with RWO: Single-replica applications with persistent state (simple, no scaling)
- Deployment with ROX: Configuration or static content distributed to many read-only replicas
6. Performance Implications#
| Access Mode | Backend | Typical Latency | Throughput | Notes |
|---|---|---|---|---|
| RWO (block) | Local SSD / NVMe | Lowest (<0.1 ms) | Highest | Direct device access |
| RWO (block) | Ceph RBD / Longhorn | Low (0.5-2 ms) | High | Network replication adds latency |
| RWO (block) | Cloud EBS/PD | Low-Moderate (1-5 ms) | Moderate | Network-attached block |
| ROX (any) | NFS/CephFS | Moderate (1-10 ms) | Moderate | Read caching helps |
| RWX (NFS) | NFS / Longhorn NFS | Moderate (2-10 ms) | Moderate | NFS protocol overhead |
| RWX (CephFS) | CephFS | Moderate (1-5 ms) | High | Parallel metadata servers |
| RWOP (block) | Any block backend | Same as RWO | Same as RWO | No additional overhead vs RWO |
Key considerations:
- RWX via NFS adds a protocol translation layer, increasing latency compared to direct block access
- Longhorn RWX creates an NFS share manager pod per volume, consuming additional CPU and memory
- For read-heavy workloads, ROX with client-side caching can outperform RWX
- RWOP guarantees exclusive access, enabling filesystem optimizations (no lock contention)
7. Choosing the Right Access Mode#
Decision process:
- Does the workload need write access?
- No: Use ROX
- Yes: Continue
- Do multiple pods on different nodes need write access?
- No: Use RWO (or RWOP for strict single-pod guarantee)
- Yes: Use RWX
- Is the storage backend compatible?
- Check the CSI driver support table above
- If RWX is needed but the backend only supports RWO, consider NFS on top of block storage or a different backend
- Are you using StatefulSet or Deployment?
- StatefulSet: RWO is usually sufficient since each pod gets its own PVC
- Deployment with replicas > 1: RWX is required if pods land on different nodes
Troubleshooting#
| Issue | Cause | Solution |
|---|---|---|
PVC stuck in Pending | No PV matches the requested access mode and StorageClass | Verify the StorageClass provisioner supports the requested access mode; check PV availability |
Multi-Attach error for volume | Two pods on different nodes trying to mount an RWO volume | Change to RWX access mode, or use a StatefulSet so each pod gets its own PVC |
Pod stuck in ContainerCreating with volume mount error | CSI driver cannot attach the volume | Check CSI driver pods are running; inspect events with kubectl describe pod <pod> |
| RWX volume has poor write performance | NFS protocol overhead or contention | Increase NFS server threads; use nconnect mount option; consider CephFS for better parallel performance |
| Data corruption with RWX | Application not designed for concurrent writes | Use application-level locking (flock), or switch to a database; RWX does not guarantee consistency between writers |
| RWOP not available | Kubernetes version <1.27 or CSI driver does not support it | Upgrade Kubernetes to 1.27+; check CSI driver documentation for RWOP support |
| Volume expansion fails | StorageClass does not have allowVolumeExpansion: true | Edit the StorageClass to enable expansion; some backends require offline expansion (pod must be deleted first) |