Learning Kubernetes - Sharing Volume Between Pods
Episode 22 of 47

Learning Kubernetes - Sharing Volume Between Pods

In this episode, we'll discuss how to share volumes between Pods in Kubernetes. We'll learn about PersistentVolumes, PersistentVolumeClaims, StorageClasses, and strategies for sharing data across Pods.

Arman Dwi Pangestu
Arman Dwi PangestuMarch 28, 2026
0 views
8 min read

Introduction

In the previous episode, we learned about Secret. In episode 22, we'll discuss Sharing Volumes Between Pods, exploring how to share data across multiple Pods using PersistentVolumes and PersistentVolumeClaims.

Note: Here I'll be using a Kubernetes Cluster installed through K3s.

While volumes in the previous episode were Pod-scoped, sharing data between Pods requires a different approach. PersistentVolumes provide cluster-level storage resources that can be claimed by multiple Pods, enabling data sharing and persistence beyond Pod lifecycle.

What Is Volume Sharing?

Volume Sharing is the ability for multiple Pods to access the same storage resource. This enables scenarios like shared file storage, collaborative workloads, and data exchange between applications.

Think of shared volumes like a network drive in an office - multiple employees (Pods) can access the same files, collaborate on documents, and share data without duplicating storage.

Key characteristics of Shared Volumes:

  • Multi-Pod access - Multiple Pods can mount the same volume
  • Data persistence - Data survives Pod deletion and recreation
  • Cluster-scoped - Storage exists independently of Pods
  • Access modes - Control read/write permissions
  • Dynamic provisioning - Automatic volume creation
  • Storage abstraction - Decouples storage from Pods

Why Share Volumes Between Pods?

Sharing volumes solves several important use cases:

  • Shared file storage - Multiple Pods accessing common files
  • Data exchange - Transfer data between applications
  • Collaborative workloads - Multiple workers processing shared data
  • Centralized logging - Aggregate logs from multiple Pods
  • Shared configuration - Common config files across Pods
  • Media storage - Shared uploads, images, videos
  • Database replication - Shared storage for database clusters
  • Backup and restore - Centralized backup storage

Without shared volumes, each Pod would need its own storage, making data sharing complex and inefficient.

PersistentVolume (PV)

A PersistentVolume is a cluster-level storage resource provisioned by an administrator or dynamically created by StorageClasses.

PersistentVolume Characteristics

  • Cluster resource - Independent of any Pod
  • Lifecycle independent - Survives Pod deletion
  • Provisioned storage - Pre-allocated or dynamically created
  • Access modes - Defines how volume can be mounted
  • Reclaim policy - What happens when released
  • Storage capacity - Size of the volume

Creating a PersistentVolume

Example: NFS PersistentVolume

Kubernetesnfs-pv.yml
apiVersion: v1
kind: PersistentVolume
metadata:
    name: nfs-pv
spec:
    capacity:
        storage: 10Gi
    accessModes:
        - ReadWriteMany
    persistentVolumeReclaimPolicy: Retain
    nfs:
        server: nfs-server.example.com
        path: /shared/data

Example: HostPath PersistentVolume (for testing)

Kuberneteshostpath-pv.yml
apiVersion: v1
kind: PersistentVolume
metadata:
    name: hostpath-pv
spec:
    capacity:
        storage: 5Gi
    accessModes:
        - ReadWriteOnce
    persistentVolumeReclaimPolicy: Delete
    hostPath:
        path: /mnt/data
        type: DirectoryOrCreate

Warning

Warning: hostPath PersistentVolumes are only suitable for single-node testing. Use network storage for production.

PersistentVolume Access Modes

Access modes determine how the volume can be mounted:

ReadWriteOnce (RWO)

  • Mounted read-write by a single node
  • Multiple Pods on the same node can access
  • Most common for block storage
Kubernetesyml
accessModes:
    - ReadWriteOnce

ReadOnlyMany (ROX)

  • Mounted read-only by many nodes
  • Useful for shared configuration or static content
Kubernetesyml
accessModes:
    - ReadOnlyMany

ReadWriteMany (RWX)

  • Mounted read-write by many nodes
  • Required for true multi-Pod sharing
  • Needs network filesystem (NFS, CephFS, GlusterFS)
Kubernetesyml
accessModes:
    - ReadWriteMany

ReadWriteOncePod (RWOP)

  • Mounted read-write by a single Pod only
  • Kubernetes 1.22+ feature
Kubernetesyml
accessModes:
    - ReadWriteOncePod

Reclaim Policies

Defines what happens to the volume when the claim is deleted:

Retain

  • Volume is not deleted
  • Data is preserved
  • Manual cleanup required
Kubernetesyml
persistentVolumeReclaimPolicy: Retain

Delete

  • Volume is automatically deleted
  • Data is lost
  • Common for dynamically provisioned volumes
Kubernetesyml
persistentVolumeReclaimPolicy: Delete

Recycle (Deprecated)

  • Volume is scrubbed and made available again
  • Not recommended, use Delete instead

PersistentVolumeClaim (PVC)

A PersistentVolumeClaim is a request for storage by a user. It's like a Pod requesting CPU and memory, but for storage.

PersistentVolumeClaim Characteristics

  • Storage request - Specifies size and access mode
  • Namespace-scoped - Belongs to a namespace
  • Binding - Automatically bound to matching PV
  • Dynamic provisioning - Can trigger automatic PV creation
  • Used by Pods - Pods reference PVCs, not PVs directly

Creating a PersistentVolumeClaim

Example: Basic PVC

Kubernetesbasic-pvc.yml
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
    name: data-pvc
spec:
    accessModes:
        - ReadWriteOnce
    resources:
        requests:
            storage: 5Gi

Example: PVC with StorageClass

Kubernetespvc-with-storageclass.yml
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
    name: fast-storage-pvc
spec:
    accessModes:
        - ReadWriteOnce
    storageClassName: fast-ssd
    resources:
        requests:
            storage: 10Gi

Example: Shared Storage PVC

Kubernetesshared-pvc.yml
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
    name: shared-data-pvc
spec:
    accessModes:
        - ReadWriteMany
    resources:
        requests:
            storage: 20Gi

PVC Binding

When you create a PVC, Kubernetes finds a matching PV:

  1. Match criteria: capacity, access modes, storage class
  2. Binding: PVC is bound to PV
  3. Exclusive: One PVC per PV (unless ReadWriteMany)
  4. Status: PVC shows "Bound" when successful

Check PVC status:

Kubernetesbash
sudo kubectl get pvc

Output:

Kubernetesbash
NAME           STATUS   VOLUME      CAPACITY   ACCESS MODES   STORAGECLASS   AGE
data-pvc       Bound    pv-001      5Gi        RWO            standard       2m
shared-pvc     Bound    nfs-pv      20Gi       RWX            nfs            1m

Using PVC in Pods

Pods reference PVCs to mount persistent storage.

Single Pod with PVC

Kubernetespod-with-pvc.yml
apiVersion: v1
kind: Pod
metadata:
    name: app-pod
spec:
    containers:
        - name: app
          image: nginx:1.25
          volumeMounts:
              - name: data
                mountPath: /usr/share/nginx/html
    volumes:
        - name: data
          persistentVolumeClaim:
              claimName: data-pvc

Multiple Pods Sharing PVC (ReadWriteMany)

Kubernetesshared-pods.yml
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
    name: shared-storage
spec:
    accessModes:
        - ReadWriteMany
    resources:
        requests:
            storage: 10Gi
---
apiVersion: v1
kind: Pod
metadata:
    name: writer-pod
spec:
    containers:
        - name: writer
          image: busybox:1.36
          command:
              - sh
              - -c
              - while true; do date >> /data/log.txt; sleep 5; done
          volumeMounts:
              - name: shared
                mountPath: /data
    volumes:
        - name: shared
          persistentVolumeClaim:
              claimName: shared-storage
---
apiVersion: v1
kind: Pod
metadata:
    name: reader-pod
spec:
    containers:
        - name: reader
          image: busybox:1.36
          command:
              - sh
              - -c
              - tail -f /data/log.txt
          volumeMounts:
              - name: shared
                mountPath: /data
    volumes:
        - name: shared
          persistentVolumeClaim:
              claimName: shared-storage

Both Pods can access the same data simultaneously.

StorageClass

A StorageClass provides a way to describe different storage types and enables dynamic provisioning.

StorageClass Characteristics

  • Dynamic provisioning - Automatically creates PVs
  • Storage types - Define different performance tiers
  • Parameters - Configure storage backend
  • Default class - Used when no class specified
  • Provisioner - Backend that creates volumes

Creating a StorageClass

Example: Local Storage

Kuberneteslocal-storageclass.yml
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
    name: local-storage
provisioner: kubernetes.io/no-provisioner
volumeBindingMode: WaitForFirstConsumer

Example: NFS Storage

Kubernetesnfs-storageclass.yml
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
    name: nfs-storage
provisioner: nfs.csi.k8s.io
parameters:
    server: nfs-server.example.com
    share: /shared
reclaimPolicy: Retain
volumeBindingMode: Immediate

Example: AWS EBS (for AWS)

Kubernetesaws-ebs-storageclass.yml
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
    name: fast-ssd
provisioner: ebs.csi.aws.com
parameters:
    type: gp3
    iops: "3000"
    throughput: "125"
reclaimPolicy: Delete
volumeBindingMode: WaitForFirstConsumer

Default StorageClass

Set a default StorageClass:

Kubernetesdefault-storageclass.yml
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
    name: standard
    annotations:
        storageclass.kubernetes.io/is-default-class: "true"
provisioner: kubernetes.io/no-provisioner
volumeBindingMode: WaitForFirstConsumer

Check default StorageClass:

Kubernetesbash
sudo kubectl get storageclass

Output:

Kubernetesbash
NAME                 PROVISIONER                    RECLAIMPOLICY   VOLUMEBINDINGMODE      AGE
standard (default)   kubernetes.io/no-provisioner   Delete          WaitForFirstConsumer   5d
fast-ssd             ebs.csi.aws.com                Delete          WaitForFirstConsumer   2d

Practical Examples

Example 1: Shared Web Content

Kubernetesshared-web-content.yml
# PersistentVolume
apiVersion: v1
kind: PersistentVolume
metadata:
    name: web-content-pv
spec:
    capacity:
        storage: 10Gi
    accessModes:
        - ReadWriteMany
    persistentVolumeReclaimPolicy: Retain
    nfs:
        server: nfs-server.example.com
        path: /web/content
---
# PersistentVolumeClaim
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
    name: web-content-pvc
spec:
    accessModes:
        - ReadWriteMany
    resources:
        requests:
            storage: 10Gi
---
# Deployment with multiple replicas sharing volume
apiVersion: apps/v1
kind: Deployment
metadata:
    name: web-server
spec:
    replicas: 3
    selector:
        matchLabels:
            app: web
    template:
        metadata:
            labels:
                app: web
        spec:
            containers:
                - name: nginx
                  image: nginx:1.25
                  ports:
                      - containerPort: 80
                  volumeMounts:
                      - name: content
                        mountPath: /usr/share/nginx/html
            volumes:
                - name: content
                  persistentVolumeClaim:
                      claimName: web-content-pvc

All three replicas share the same web content.

Example 2: Shared Upload Storage

Kubernetesshared-uploads.yml
# PVC for shared uploads
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
    name: uploads-pvc
spec:
    accessModes:
        - ReadWriteMany
    resources:
        requests:
            storage: 50Gi
---
# API server that receives uploads
apiVersion: apps/v1
kind: Deployment
metadata:
    name: api-server
spec:
    replicas: 2
    selector:
        matchLabels:
            app: api
    template:
        metadata:
            labels:
                app: api
        spec:
            containers:
                - name: api
                  image: myapi:latest
                  ports:
                      - containerPort: 8080
                  volumeMounts:
                      - name: uploads
                        mountPath: /var/uploads
            volumes:
                - name: uploads
                  persistentVolumeClaim:
                      claimName: uploads-pvc
---
# Worker that processes uploads
apiVersion: apps/v1
kind: Deployment
metadata:
    name: upload-processor
spec:
    replicas: 3
    selector:
        matchLabels:
            app: processor
    template:
        metadata:
            labels:
                app: processor
        spec:
            containers:
                - name: processor
                  image: processor:latest
                  volumeMounts:
                      - name: uploads
                        mountPath: /var/uploads
                        readOnly: true
            volumes:
                - name: uploads
                  persistentVolumeClaim:
                      claimName: uploads-pvc

API servers write uploads, processors read them.

Example 3: Shared Configuration

Kubernetesshared-config.yml
# PVC for shared configuration
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
    name: config-pvc
spec:
    accessModes:
        - ReadOnlyMany
    resources:
        requests:
            storage: 1Gi
---
# Config manager (writes config)
apiVersion: v1
kind: Pod
metadata:
    name: config-manager
spec:
    containers:
        - name: manager
          image: config-manager:latest
          volumeMounts:
              - name: config
                mountPath: /config
    volumes:
        - name: config
          persistentVolumeClaim:
              claimName: config-pvc
---
# Applications (read config)
apiVersion: apps/v1
kind: Deployment
metadata:
    name: app
spec:
    replicas: 5
    selector:
        matchLabels:
            app: myapp
    template:
        metadata:
            labels:
                app: myapp
        spec:
            containers:
                - name: app
                  image: myapp:latest
                  volumeMounts:
                      - name: config
                        mountPath: /etc/app/config
                        readOnly: true
            volumes:
                - name: config
                  persistentVolumeClaim:
                      claimName: config-pvc

Example 4: StatefulSet with Shared and Individual Storage

Kubernetesstatefulset-mixed-storage.yml
# Shared PVC for common data
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
    name: shared-data-pvc
spec:
    accessModes:
        - ReadWriteMany
    resources:
        requests:
            storage: 20Gi
---
# StatefulSet with both shared and individual storage
apiVersion: apps/v1
kind: StatefulSet
metadata:
    name: database
spec:
    serviceName: database
    replicas: 3
    selector:
        matchLabels:
            app: db
    template:
        metadata:
            labels:
                app: db
        spec:
            containers:
                - name: postgres
                  image: postgres:15
                  volumeMounts:
                      # Individual storage per Pod
                      - name: data
                        mountPath: /var/lib/postgresql/data
                      # Shared storage across all Pods
                      - name: shared
                        mountPath: /shared
            volumes:
                - name: shared
                  persistentVolumeClaim:
                      claimName: shared-data-pvc
    # Individual PVC per Pod
    volumeClaimTemplates:
        - metadata:
              name: data
          spec:
              accessModes:
                  - ReadWriteOnce
              resources:
                  requests:
                      storage: 10Gi

Volume Expansion

Expand existing PVC size (if supported by StorageClass).

Enable Volume Expansion

Kubernetesexpandable-storageclass.yml
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
    name: expandable-storage
provisioner: kubernetes.io/no-provisioner
allowVolumeExpansion: true

Expand PVC

Edit the PVC to increase size:

Kubernetesbash
sudo kubectl edit pvc data-pvc

Change storage request:

Kubernetesyml
spec:
    resources:
        requests:
            storage: 20Gi  # Increased from 10Gi

Or patch directly:

Kubernetesbash
sudo kubectl patch pvc data-pvc -p '{"spec":{"resources":{"requests":{"storage":"20Gi"}}}}'

Important

Important: Volume expansion only increases size, never decreases. Some storage types require Pod restart to recognize new size.

Common Mistakes and Pitfalls

Mistake 1: Wrong Access Mode for Sharing

Problem: Using ReadWriteOnce for multi-Pod sharing.

Solution: Use ReadWriteMany for true sharing:

Kubernetesyml
# Bad: Can't share across nodes
accessModes:
    - ReadWriteOnce
 
# Good: Can share across nodes
accessModes:
    - ReadWriteMany

Mistake 2: No Matching PV

Problem: PVC stays in Pending state.

Solution: Create matching PV or use dynamic provisioning:

Kubernetesbash
# Check PVC status
sudo kubectl describe pvc <pvc-name>
 
# Look for events explaining why binding failed

Mistake 3: Insufficient Storage

Problem: PVC requests more than PV provides.

Solution: Match PVC request to available PV capacity:

Kubernetesyml
# PV capacity
capacity:
    storage: 10Gi
 
# PVC request (must be <= PV capacity)
resources:
    requests:
        storage: 10Gi

Mistake 4: Using hostPath for Multi-Node

Problem: hostPath only works on single node.

Solution: Use network storage for multi-node clusters:

Kubernetesyml
# Bad: hostPath for production
hostPath:
    path: /data
 
# Good: NFS for production
nfs:
    server: nfs-server.example.com
    path: /shared/data

Mistake 5: Forgetting Reclaim Policy

Problem: Data deleted when PVC is removed.

Solution: Use Retain for important data:

Kubernetesyml
persistentVolumeReclaimPolicy: Retain

Best Practices

Use StorageClasses

Define storage tiers for different needs:

Kubernetesyml
# Fast SSD for databases
storageClassName: fast-ssd
 
# Standard HDD for logs
storageClassName: standard
 
# Shared NFS for uploads
storageClassName: nfs-storage

Set Appropriate Access Modes

Choose based on sharing requirements:

Kubernetesyml
# Single Pod, single node
accessModes:
    - ReadWriteOnce
 
# Multiple Pods, multiple nodes
accessModes:
    - ReadWriteMany
 
# Read-only sharing
accessModes:
    - ReadOnlyMany

Use Retain for Production Data

Protect important data:

Kubernetesyml
persistentVolumeReclaimPolicy: Retain

Label PVs and PVCs

Organize storage resources:

Kubernetesyml
metadata:
    name: database-pv
    labels:
        type: database
        environment: production
        tier: fast

Monitor Storage Usage

Check PVC usage regularly:

Kubernetesbash
sudo kubectl get pvc
sudo kubectl describe pvc <pvc-name>

Plan for Growth

Enable volume expansion:

Kubernetesyml
allowVolumeExpansion: true

Use Separate PVCs

Don't share PVCs unnecessarily:

Kubernetesyml
# Good: Separate PVCs for different purposes
- name: database-pvc
- name: logs-pvc
- name: uploads-pvc

Viewing Storage Resources

Get PersistentVolumes

Kubernetesbash
sudo kubectl get pv

Get PersistentVolumeClaims

Kubernetesbash
sudo kubectl get pvc
sudo kubectl get pvc -A  # All namespaces

Describe PV

Kubernetesbash
sudo kubectl describe pv <pv-name>

Describe PVC

Kubernetesbash
sudo kubectl describe pvc <pvc-name>

Get StorageClasses

Kubernetesbash
sudo kubectl get storageclass

Check Volume Usage

Kubernetesbash
sudo kubectl exec <pod-name> -- df -h

Troubleshooting Shared Volumes

PVC Pending

Check why PVC isn't binding:

Kubernetesbash
sudo kubectl describe pvc <pvc-name>

Common causes:

  • No matching PV available
  • Insufficient capacity
  • Access mode mismatch
  • StorageClass not found

Permission Denied

Set proper security context:

Kubernetesyml
securityContext:
    fsGroup: 2000
    runAsUser: 1000

Data Not Visible

Verify PVC is bound and mounted:

Kubernetesbash
sudo kubectl get pvc
sudo kubectl describe pod <pod-name>

Volume Full

Check disk usage and expand if needed:

Kubernetesbash
sudo kubectl exec <pod-name> -- df -h
sudo kubectl patch pvc <pvc-name> -p '{"spec":{"resources":{"requests":{"storage":"20Gi"}}}}'

Conclusion

In episode 22, we've explored Sharing Volumes Between Pods in Kubernetes. We've learned about PersistentVolumes, PersistentVolumeClaims, StorageClasses, and strategies for sharing data across Pods.

Key takeaways:

  • PersistentVolume (PV) is cluster-level storage resource
  • PersistentVolumeClaim (PVC) is a request for storage
  • Pods reference PVCs, not PVs directly
  • ReadWriteMany access mode enables true multi-Pod sharing
  • StorageClass enables dynamic provisioning
  • Reclaim policies control data retention (Retain, Delete)
  • Use network storage (NFS, CephFS) for multi-node sharing
  • Volume expansion allows growing storage size
  • Access modes control how volumes are mounted (RWO, ROX, RWX, RWOP)
  • Default StorageClass used when none specified
  • Label and organize storage resources
  • Monitor storage usage regularly
  • Use Retain policy for production data

Sharing volumes between Pods is essential for collaborative workloads and data exchange in Kubernetes. By understanding PersistentVolumes and PersistentVolumeClaims, you can design robust storage architectures for your applications.

Are you getting a clearer understanding of Sharing Volumes in Kubernetes? Keep your learning momentum going and look forward to the next episode!