Kubernetes Shared Volumes: A Comprehensive Guide
Table of Contents
- Prerequisites
- Introduction
- Core Concepts
- Volume Types
- Access Modes Explained
- Hands-On Examples
- Best Practices
- Troubleshooting
- Summary and Next Steps
Introduction
Shared volumes in Kubernetes represent a fundamental storage mechanism that enables containers and pods to exchange data in a coordinated manner. This capability is essential for building distributed applications where multiple components need to access shared resources or coordinate asynchronously.
This comprehensive guide covers the conceptual foundations of Kubernetes volumes, explores different volume types, and provides detailed hands-on examples. Whether you are managing temporary data within a single pod or orchestrating communication between multiple microservices, understanding volumes is crucial for effective Kubernetes deployments.
For the official Kubernetes documentation on volumes, refer to the Kubernetes Volumes documentation.
Prerequisites
Before working through the hands-on examples in this guide, ensure you have the following tools installed:
-
kind: A tool for running local Kubernetes clusters using Docker containers. Kind simplifies learning and testing Kubernetes concepts without requiring a full cluster setup.
- Download and installation instructions: kind Getting Started Guide
- Verify installation:
kind version
-
kubectl: The Kubernetes command-line tool for interacting with Kubernetes clusters
- Download and installation instructions: Kubernetes kubectl Documentation
- Verify installation:
kubectl version --client
-
Docker: Required by kind to create containerized Kubernetes cluster nodes
- Download and installation instructions: Docker Desktop Installation
- Verify installation:
docker --version
Quick Start with kind
Create a local Kubernetes cluster for practicing these examples:
# Create a new kind cluster
kind create cluster --name volumes-demo
# Verify cluster is running
kubectl cluster-info --context kind-volumes-demo
# Switch to the new cluster context
kubectl config use-context kind-volumes-demo
# When finished, delete the cluster
kind delete cluster --name volumes-demo
Core Concepts
What is a Volume?
A volume is a directory, possibly with data in it, which is accessible to the containers in a pod. It is a way to mount storage into a container.
Pod Lifecycle vs Volume Lifecycle
Understanding the relationship between pod and volume lifecycles is critical when designing storage strategies. When a pod is terminated, ephemeral volumes are also deleted. However, persistent volumes survive pod deletion and can be remounted to new pods, ensuring data durability.
This distinction determines which volume type is appropriate for your use case:
- emptyDir volumes are created when a pod starts and deleted when the pod terminates. These are suitable for temporary data that does not need to persist.
- PersistentVolumes exist independently of any pod. Data written to persistent storage remains available even after pod deletion, making them ideal for production environments requiring data durability.
Why Share Volumes?
In distributed systems, components frequently need to exchange data or coordinate their activities. Consider a data processing pipeline with multiple stages:
A producer service generates data and writes it to a shared location. A consumer service monitors this location, processes incoming data, and generates results. A separate cleanup service archives processed files according to retention policies.
Without shared volumes, these independent pods would remain isolated with no mechanism for communication. By leveraging shared volumes, these components coordinate asynchronously through filesystem operations, creating a loosely coupled yet efficient distributed system.
Volume Types
1. emptyDir - Temporary Shared Storage
What It Does
- Creates an empty directory when a pod starts
- Automatically deleted when the pod terminates
- Shared among all containers in the same pod
Use Cases
- Temporary scratch space
- Sharing data between sidecar containers
- Cache storage
- Logs that don't need to persist
Key Characteristics
volumes:
- name: temp-storage
emptyDir: {}
Considerations:
- Cannot be shared across multiple pods
- Data is lost when the pod is deleted
- Offers low-latency access without storage provisioning overhead
- Suitable for non-critical temporary data
For more details on emptyDir volumes, see the official Kubernetes emptyDir documentation.
2. PersistentVolumeClaim (PVC) - Durable Shared Storage
What It Does
- Requests persistent storage from the cluster
- Storage survives pod deletion and recreation
- Can be shared across multiple pods (with proper access modes)
- Decouples storage provisioning from pod lifecycle
Use Cases
- Production databases
- Shared data for multiple pods
- Long-term data retention
- Asynchronous application communication
Key Characteristics
volumes:
- name: persistent-storage
persistentVolumeClaim:
claimName: my-pvc
Advantages:
- Data persists beyond the pod lifecycle, surviving pod deletions and recreations
- Enables data sharing across multiple pods when configured with appropriate access modes
- Suitable for production workloads requiring data durability
- Supports multiple storage backends including NFS, cloud block storage, and distributed filesystems
For comprehensive details on PersistentVolumeClaims, consult the official PVC documentation.
3. hostPath - Node Local Storage
What It Does
- Mounts a path from the host node into the pod
- Useful for accessing host-level resources
Use Cases
- Docker socket access (for Docker-in-Docker)
- Host logs monitoring
- Node-level debugging
Considerations
- Not portable across nodes; volumes are tied to the host where they are mounted
- Introduces security considerations as pods gain direct access to host filesystem paths
- Not recommended for multi-node clusters as data may not be accessible from other nodes
- Best reserved for debugging and specialized use cases requiring direct host access
Access Modes Explained
Access modes define how a PersistentVolume can be accessed and are critical when designing multi-pod storage solutions. Different access modes support different deployment scenarios and determine whether multiple pods can simultaneously access the same volume.
| Access Mode | Abbreviation | Description | Multi-Pod Support |
|---|---|---|---|
| ReadWriteOnce | RWO | Single pod can read and write | Not applicable |
| ReadOnlyMany | ROX | Multiple pods can read; no write access | Yes (read-only) |
| ReadWriteMany | RWX | Multiple pods can read and write | Yes |
For detailed information on access modes, refer to the Kubernetes access modes documentation.
Selecting the Appropriate Access Mode
The access mode you choose depends on your deployment pattern. For single-pod deployments where only one instance needs access:
accessModes:
- ReadWriteOnce # Appropriate for single-pod deployments
When multiple independent pods must simultaneously write to and read from the same storage:
accessModes:
- ReadWriteMany # Required for multi-pod write scenarios
Hands-On Examples
Example 1: Shared Volume Within a Single Pod (emptyDir)
Scenario: Two containers in the same pod need to share data. A "writer" container creates a file, and a "reader" container reads it.
Step 1: Create the Pod Manifest
# Create a file: shared-volume-pod.yml
cat > shared-volume-pod.yml << 'EOF'
apiVersion: v1
kind: Pod
metadata:
name: shared-demo
spec:
containers:
- name: writer
image: busybox:latest
command:
- sh
- -c
- |
echo "Hello from writer container" > /shared/msg.txt
sleep 3600 # Keep container running
volumeMounts:
- name: shared-storage
mountPath: /shared
- name: reader
image: busybox:latest
command:
- sh
- -c
- |
echo "Waiting for message..."
while [ ! -f /shared/msg.txt ]; do
echo "File not ready, waiting..."
sleep 2
done
echo "Message received:"
cat /shared/msg.txt
sleep 3600 # Keep container running
volumeMounts:
- name: shared-storage
mountPath: /shared
volumes:
- name: shared-storage
emptyDir: {}
EOF
Step 2: Deploy the Pod
kubectl apply -f shared-volume-pod.yml
Step 3: Verify Communication
# Check pod status
kubectl get pod shared-demo
# View logs from writer container
kubectl logs shared-demo -c writer
# View logs from reader container
kubectl logs shared-demo -c reader
# Verify file exists in shared volume
kubectl exec shared-demo -c reader -- cat /shared/msg.txt
# Verify from writer's perspective
kubectl exec shared-demo -c writer -- ls -la /shared/
Expected Output
reader logs:
Waiting for message...
Message received:
Hello from writer container
How It Works
This example demonstrates intra-pod volume sharing. The pod contains two containers, both mounting the same emptyDir volume at the path /shared. When the writer container creates a file at /shared/msg.txt, the reader container can immediately access it through the same mount path.
The volume acts as a shared filesystem namespace, allowing both containers to communicate through file operations without requiring network-based communication protocols. This mechanism is particularly useful for tightly coupled containers that need to share state or coordinate work within a single pod.
Cleanup
kubectl delete pod shared-demo
Example 2: Single Pod with PersistentVolumeClaim (Data Persistence)
Scenario: Your application needs to store data that survives pod restarts. This is the pattern used in production systems.
Step 1: Create the PersistentVolumeClaim
# File: pvc.yml
cat > pvc.yml << 'EOF'
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: app-storage-pvc
spec:
accessModes:
- ReadWriteOnce # Only one pod, so RWO is sufficient
resources:
requests:
storage: 1Gi # Request 1 gigabyte
# Note: storageClassName may need to match your cluster's configuration
# For Docker Desktop: use 'docker-desktop' or 'standard'
# For Minikube: use 'standard'
storageClassName: standard
EOF
Step 2: Create the Pod Using the PVC
# File: pvc-pod.yml
cat > pvc-pod.yml << 'EOF'
apiVersion: v1
kind: Pod
metadata:
name: persistent-writer
spec:
containers:
- name: writer
image: busybox:latest
command:
- sh
- -c
- |
echo "Data written at: $(date)" >> /data/persistent-data.txt
cat /data/persistent-data.txt
sleep 3600
volumeMounts:
- name: persistent-storage
mountPath: /data
volumes:
- name: persistent-storage
persistentVolumeClaim:
claimName: app-storage-pvc
EOF
Step 3: Deploy
# Create the PVC first
kubectl apply -f pvc.yml
# Wait for PVC to be bound
kubectl get pvc
# Then create the pod
kubectl apply -f pvc-pod.yml
Step 4: Verify Data Persistence
# Check the content written
kubectl exec persistent-writer -- cat /data/persistent-data.txt
# Delete the pod
kubectl delete pod persistent-writer
# Re-create the pod (use same manifest)
kubectl apply -f pvc-pod.yml
# Check that the data still exists!
kubectl exec persistent-writer -- cat /data/persistent-data.txt
Expected Behavior
First run output:
Data written at: Sun Aug 17 10:30:45 UTC 2026
After pod deletion and recreation:
Data written at: Sun Aug 17 10:30:45 UTC 2026
Data written at: Sun Aug 17 10:35:22 UTC 2026
(Original data persists; new data appended)
Data Durability and Pod Recreation
A key advantage of PersistentVolumeClaims is data durability across pod lifecycle events. When a pod is deleted, the persistent volume remains intact with all data preserved. When a new pod is created with the same PVC specification, it automatically mounts the existing persistent volume and regains access to all previously written data.
This behavior is fundamental to Kubernetes' design for stateful applications, enabling pod replacement and recovery without data loss.
Example 3: Multiple Pods Sharing a PersistentVolume (ReadWriteMany)
Scenario: This is the most powerful use case. Multiple independent pods communicate through shared storage—the foundation of distributed systems and microservices.
Step 1: Create a Shared PVC (ReadWriteMany)
# File: shared-pvc.yml
cat > shared-pvc.yml << 'EOF'
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: shared-data-pvc
spec:
accessModes:
- ReadWriteMany # Required for multiple pods to write concurrently
resources:
requests:
storage: 2Gi
storageClassName: standard
EOF
Step 2: Create Producer Pod (Writer)
# File: producer-pod.yml
cat > producer-pod.yml << 'EOF'
apiVersion: v1
kind: Pod
metadata:
name: data-producer
spec:
containers:
- name: producer
image: busybox:latest
command:
- sh
- -c
- |
for i in 1 2 3 4 5; do
TIMESTAMP=$(date '+%Y-%m-%d %H:%M:%S')
echo "[$TIMESTAMP] Producing record #$i" >> /shared/data.txt
sleep 2
done
echo "Producer finished!"
sleep 3600
volumeMounts:
- name: shared-vol
mountPath: /shared
volumes:
- name: shared-vol
persistentVolumeClaim:
claimName: shared-data-pvc
EOF
Step 3: Create Consumer Pod (Reader)
# File: consumer-pod.yml
cat > consumer-pod.yml << 'EOF'
apiVersion: v1
kind: Pod
metadata:
name: data-consumer
spec:
containers:
- name: consumer
image: busybox:latest
command:
- sh
- -c
- |
echo "Consumer waiting for data..."
sleep 3 # Let producer start
while true; do
if [ -f /shared/data.txt ]; then
echo "=== Data received ==="
cat /shared/data.txt
echo "=== End of data ==="
else
echo "Waiting for data file..."
fi
sleep 5
done
volumeMounts:
- name: shared-vol
mountPath: /shared
volumes:
- name: shared-vol
persistentVolumeClaim:
claimName: shared-data-pvc
EOF
Step 4: Deploy Everything
# Create the shared PVC
kubectl apply -f shared-pvc.yml
# Wait for PVC to be bound
kubectl get pvc
# Expected: shared-data-pvc Bound ...
# Deploy both pods
kubectl apply -f producer-pod.yml
kubectl apply -f consumer-pod.yml
# Verify both are running
kubectl get pods
Step 5: Monitor Communication
# Terminal 1: Watch producer logs
kubectl logs -f data-producer
# Terminal 2: Watch consumer logs
kubectl logs -f data-consumer
# Terminal 3: Check the shared file in real-time
watch kubectl exec data-consumer -- cat /shared/data.txt
Expected Flow
Time: 0s
Producer: Creates /shared/data.txt
Consumer: Waiting...
Time: 2s
Producer: Writes first record
Consumer: Sees file, reads content
Time: 4s
Producer: Writes second record
Consumer: Reads updated content
... and so on
Architecture and Communication Pattern
This multi-pod scenario illustrates the decoupled communication pattern that Kubernetes shared volumes enable. Two independent pods, the producer and consumer, operate autonomously without direct knowledge of each other.
The producer pod periodically writes data records to the shared volume. The consumer pod independently monitors the shared location, reads available data, processes it, and writes results. Neither pod requires awareness of the other's existence or operational state.
This asynchronous, filesystem-based communication pattern decouples component lifecycle management from data coordination, enabling resilient distributed systems. If the consumer pod restarts, the producer continues writing data uninterrupted. Similarly, producer failures do not affect the consumer's ability to process previously written data.
Cleanup
kubectl delete pod data-producer data-consumer
kubectl delete pvc shared-data-pvc
Example 4: Real-World Scenario - Data Pipeline (Like the Project)
This mirrors the actual pipeline in your project.
Step 1: Create Namespace
kubectl create namespace data-pipeline
Step 2: Create Shared Storage
# File: pipeline-pvc.yml
cat > pipeline-pvc.yml << 'EOF'
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
namespace: data-pipeline
name: pipeline-shared-storage
spec:
accessModes:
- ReadWriteMany
resources:
requests:
storage: 5Gi
storageClassName: standard
EOF
Step 3: Create Scraper Pod (Produces Data)
# File: scraper-pod.yml
cat > scraper-pod.yml << 'EOF'
apiVersion: v1
kind: Pod
metadata:
namespace: data-pipeline
name: json-scraper
spec:
containers:
- name: scraper
image: busybox:latest
command:
- sh
- -c
- |
mkdir -p /data/incoming /data/processed /data/failed
# Simulate scraping
echo '{"id": 1, "name": "Product A", "price": 29.99}' > /data/incoming/products-$(date +%Y-%m-%d).json
echo "[$(date)] Scraper: Generated JSON file" >> /data/logs/activity.log
sleep 3600
volumeMounts:
- name: shared
mountPath: /data
volumes:
- name: shared
persistentVolumeClaim:
claimName: pipeline-shared-storage
EOF
Step 4: Create Consumer Pod (Processes Data)
# File: consumer-pod.yml
cat > consumer-pod.yml << 'EOF'
apiVersion: v1
kind: Pod
metadata:
namespace: data-pipeline
name: json-consumer
spec:
containers:
- name: consumer
image: busybox:latest
command:
- sh
- -c
- |
mkdir -p /data/incoming /data/processed /data/reports
echo "[$(date)] Consumer: Started watching for files" >> /data/logs/activity.log
while true; do
if [ -f /data/incoming/products-*.json ]; then
FILE=$(ls /data/incoming/products-*.json 2>/dev/null | head -1)
if [ -n "$FILE" ]; then
echo "[$(date)] Consumer: Processing $FILE" >> /data/logs/activity.log
# Process file
cat "$FILE" > /data/reports/report-$(basename $FILE .json)-report.json
# Move to processed
mv "$FILE" /data/processed/
echo "[$(date)] Consumer: Completed processing" >> /data/logs/activity.log
fi
fi
sleep 5
done
volumeMounts:
- name: shared
mountPath: /data
volumes:
- name: shared
persistentVolumeClaim:
claimName: pipeline-shared-storage
EOF
Step 5: Deploy and Monitor
# Deploy everything
kubectl apply -f pipeline-pvc.yml
kubectl apply -f scraper-pod.yml
kubectl apply -f consumer-pod.yml
# Watch the pipeline in action
kubectl -n data-pipeline logs -f json-consumer
# Verify files are being processed
kubectl -n data-pipeline exec json-consumer -- ls -la /data/incoming/
kubectl -n data-pipeline exec json-consumer -- ls -la /data/processed/
kubectl -n data-pipeline exec json-consumer -- ls -la /data/reports/
# Check activity log
kubectl -n data-pipeline exec json-consumer -- tail -20 /data/logs/activity.log
Data Pipeline Architecture
This example demonstrates a complete data processing pipeline with multiple stages, each coordinating through shared persistent storage:
Best Practices
1. Select the Appropriate Volume Type
Match your storage solution to your requirements:
- emptyDir: Use for temporary data that does not need to persist beyond the pod lifecycle
- PersistentVolumeClaim: Use when data must survive pod deletion or recreation
- PersistentVolumeClaim with ReadWriteMany: Use when multiple pods must simultaneously access the same storage
- hostPath: Use sparingly for specialized scenarios requiring direct host filesystem access
For most production scenarios, PersistentVolumeClaims provide the appropriate balance of durability, portability, and multi-pod support.
2. Use PersistentVolumeClaims in Production Environments
Production systems must ensure data durability and recovery capabilities. Using emptyDir volumes in production deployments risks data loss if pods terminate unexpectedly:
# Not recommended for production: ephemeral storage
volumes:
- name: data
emptyDir: {}
# Recommended for production: persistent storage
volumes:
- name: data
persistentVolumeClaim:
claimName: my-app-pvc
PersistentVolumeClaims provide data durability, recovery mechanisms, and cross-pod data access, making them essential for stateful applications in production environments.
3. Configure Appropriate Storage Classes
Kubernetes clusters typically provide multiple storage classes with different performance and cost characteristics. Select the storage class that matches your performance and cost requirements:
# Identify available storage classes
kubectl get storageclass
# Specify the appropriate class for your workload
spec:
storageClassName: fast-ssd # For high-performance requirements (databases)
# or
storageClassName: standard # For general-purpose shared storage
Consult your cluster administrator or cloud provider documentation to understand the available storage classes and their performance characteristics.
4. Use ReadWriteMany Access Mode Judiciously
While ReadWriteMany access modes enable multi-pod storage sharing, they may introduce performance overhead compared to ReadWriteOnce. Only use ReadWriteMany when your deployment genuinely requires multiple pods to simultaneously write to the same storage:
# Use ReadWriteOnce for single-pod deployments
accessModes:
- ReadWriteOnce # Better performance for single-pod access
# Use ReadWriteMany only when multiple pods must write
accessModes:
- ReadWriteMany # Use only when necessary for multi-pod access
Performance implications vary depending on the underlying storage backend. Some storage systems handle concurrent writes efficiently, while others may experience performance degradation.
5. Monitor Volume Utilization and Performance
Implement monitoring to track storage usage and prevent capacity exhaustion:
# Check PVC status and bound status
kubectl get pvc
# Retrieve detailed PVC information including usage statistics
kubectl describe pvc my-pvc
# Monitor filesystem usage from within a container
kubectl exec my-pod -- df -h /data
Establish alerts for approaching storage capacity limits to enable proactive scaling before pods encounter write failures due to full storage.
6. Implement Backup and Disaster Recovery Strategies
Production systems must include backup and recovery procedures to protect against data loss:
# Backup PVC data from a running container
kubectl cp namespace/pod:/data ./backup-data
Document your storage strategy, backup procedures, and recovery processes in your infrastructure-as-code repository. Many organizations use automated backup solutions that work directly with their chosen storage backends (cloud snapshots, distributed filesystem replication, etc.).
7. Define Clear Storage Resource Requests
Specify explicit storage resource requests to ensure the Kubernetes scheduler can appropriately provision storage infrastructure:
# Request the minimum storage required for your application
resources:
requests:
storage: 10Gi # Requested storage allocation
limits:
storage: 20Gi # Maximum storage allowed
Accurate resource requests enable proper cluster capacity planning and prevent scenarios where pods cannot be scheduled due to insufficient storage availability.
8. Follow Proper Resource Cleanup Procedures
When decommissioning applications, follow the correct sequence to avoid orphaned resources and unintended data retention:
# Delete pods that are using the volume
kubectl delete pod my-pod
# Then delete the PersistentVolumeClaim
kubectl delete pvc my-pvc
# Finally, delete the underlying PersistentVolume if necessary
kubectl delete pv my-pv
This sequence ensures that pods are terminated before storage is deallocated, preventing write operations to deleted storage and ensuring clean resource cleanup.
Troubleshooting Common Issues
PersistentVolumeClaim Remains in Pending State
When a PVC remains pending, it indicates the cluster cannot provision the requested storage. Investigate using:
# Retrieve detailed status information and events
kubectl describe pvc my-pvc
# Verify required storage class exists
kubectl get storageclass
# Check node and cluster resource availability
kubectl get nodes
kubectl top nodes
Common causes include unavailable storage classes, insufficient cluster resources, or storage backend issues. Review the PVC events and cluster logs for specific error messages.
Pod Cannot Access Volume
If a pod cannot access a mounted volume, verify the mount configuration and permissions:
# Confirm the mount point exists within the container
kubectl exec my-pod -- ls -la /data
# Review detailed volume mount configuration
kubectl describe pod my-pod | grep -A 5 Mounts
# Verify the PVC is in Bound status
kubectl get pvc
Ensure the PVC is in Bound state and the container has appropriate permissions for the mounted path.
Data Not Accessible Across Pods
When data written by one pod is not visible to another pod, verify the access mode and PVC configuration:
# Confirm both pods reference the same PVC
kubectl describe pod pod1 | grep persistentVolumeClaim
kubectl describe pod pod2 | grep persistentVolumeClaim
# Verify access mode supports multi-pod access
kubectl get pvc -o yaml | grep accessModes
# Should show: - ReadWriteMany (for multi-pod write scenarios)
Common issues include using ReadWriteOnce access mode for multi-pod deployments, or pods writing to different directories within the same volume.
Summary
Shared volumes in Kubernetes enable a fundamental architectural pattern: decoupled, asynchronous component communication through filesystem-based data exchange. Understanding when and how to apply different volume types is essential for building reliable distributed systems.
| Concept | Application |
|---|---|
| emptyDir | Temporary intra-pod data sharing; data lifecycle tied to pod |
| PersistentVolumeClaim | Persistent storage independent of pod lifecycle |
| ReadWriteOnce | Optimal for single-pod deployments; better performance |
| ReadWriteMany | Required for multi-pod scenarios requiring concurrent data access |
| Asynchronous Communication | Pods coordinate through file operations without direct dependencies |
| Component Decoupling | Producers and consumers operate independently; loosely coupled architecture |
These storage patterns form the foundation for building resilient microservices architectures where components scale and restart independently without affecting system cohesion.
Additional Resources
- Kubernetes Volumes Official Documentation
- PersistentVolumes and PersistentVolumeClaims
- Storage Classes
- Practical examples in the
k8s-play/directory of this project visit the github repository here daily-json-pipeline
Next Steps
- Deploy and experiment with the provided examples in your cluster
- Evaluate storage classes available in your Kubernetes environment
- Design storage strategies that match your application's data durability requirements
- Implement monitoring and backup procedures for persistent storage






Top comments (0)