Migrate to RunxBuild and earn up to $50 in hosting credit on your first deposit.

Calculate your savings
unxBuild

Kubernetes Volumes: PV, PVC, StorageClass, and the Right Pattern

Sean

Platform Writer

Jul 07, 2026
7 min read

Kubernetes volumes are the storage layer for pods. The three abstractions: PersistentVolume (PV, the actual storage in the cluster), PersistentVolumeClaim (PVC, a request for storage by a pod), and StorageClass (the provisioner template that creates PVs on demand). The right pattern for most apps: a PVC backed by a StorageClass that auto-provisions. The team that runs production sets up the StorageClass once, then forgets about it; the PVC is the only object the deployment manifest references.

Kubernetes Volumes: PV, PVC, StorageClass, and the Right Pattern

Table of contents

The three abstractions

PersistentVolume (PV) - a piece of storage in the cluster, provisioned by an admin or dynamically by a StorageClass. Has a capacity, an access mode, a reclaim policy, and a storage class.

PersistentVolumeClaim (PVC) - a request for storage by a user (or a pod). Names a size, an access mode, and optionally a storage class. When a PVC is created, Kubernetes binds it to a matching PV (static) or triggers dynamic provisioning (via StorageClass).

StorageClass - a template for dynamic provisioning. Names a provisioner (aws-ebs, gce-pd, nfs-csi, etc.) and parameters. When a PVC references a StorageClass, the provisioner creates a new PV automatically.

The team that has a managed Kubernetes (EKS, GKE, AKS, RunxBuild) gets a default StorageClass out of the box. The team that runs k8s on bare metal installs a CSI driver and creates the StorageClass.

The right pattern: StorageClass + PVC + pod

Most apps just need a PVC:


apiVersion: v1

kind: PersistentVolumeClaim

metadata:

  name: myapp-data

spec:

  accessModes: ["ReadWriteOnce"]

  resources:

    requests:

      storage: 10Gi

The accessModes are ReadWriteOnce (RWO, one node can mount read-write), ReadOnlyMany (ROX, many nodes can mount read-only), ReadWriteMany (RWX, many nodes can mount read-write - requires NFS or a distributed filesystem).

Mount in a pod:


volumes:

  - name: data

    persistentVolumeClaim:

      claimName: myapp-data

containers:

  - name: myapp

    volumeMounts:

      - name: data

        mountPath: /var/lib/myapp

That’s the whole pattern. The team that deploys a stateful app uses this every time.

StorageClass with parameters

A StorageClass controls how PVs are provisioned. Example for AWS EBS:


apiVersion: storage.k8s.io/v1

kind: StorageClass

metadata:

  name: fast-ssd

provisioner: kubernetes.io/aws-ebs

parameters:

  type: gp3

  iops: "3000"

  throughput: "125"

  encrypted: "true"

reclaimPolicy: Delete

volumeBindingMode: WaitForFirstConsumer

The team that has multiple tiers (fast SSD, slow HDD, replicated) sets up a StorageClass per tier and references the right one from each PVC.

Access modes: when to use each

ReadWriteOnce (RWO) - the default. One node mounts the volume read-write. Most databases (Postgres, MySQL, Mongo) use RWO. The team that picks RWO for a single-pod database is correct.

ReadOnlyMany (ROX) - many nodes mount read-only. Static assets, configuration data, read-only sidecars.

ReadWriteMany (RWX) - many nodes mount read-write. Requires a distributed filesystem (NFS, CephFS, GlusterFS, EFS). The team that runs a clustered database (Cassandra, Elasticsearch) uses RWX; the team that runs a single-instance Postgres does not need it.

Reclaim policy: what happens to the volume when the PVC is deleted

Retain - the PV is kept, the data is preserved. The team that has production data uses Retain. The admin has to manually clean up.

Delete - the PV and the underlying storage are deleted when the PVC is deleted. The team that runs dev/staging uses Delete for fast cleanup.

Recycle (deprecated) - basic scrub and re-provision. Most provisioners do not support it anymore.

The team that uses dynamic provisioning with Delete loses data when the PVC is deleted. The right pick: Retain for anything you care about, Delete for ephemeral data.

FAQ

Why is my PVC stuck in Pending?

Either no PV matches (size too big, access mode not supported, wrong storage class) or the dynamic provisioner cannot create a volume (quota exceeded, no backing storage, IAM permission). The team that debugs Pending runs kubectl describe pvc <name> to read the events.

Can I resize a PVC?

Yes, if the storage class allows it (most do). Edit the PVC: kubectl edit pvc <name>, change spec.resources.requests.storage. The volume is resized (if the underlying provider supports online expansion).

What is the difference between a Volume and a PVC?

A Volume is the in-pod mount, declared in the pod spec. A PVC is the cluster-level request that backs the volume. The pod references the PVC, the PVC references a PV (directly or via StorageClass).

Should I use a single PVC for everything?

No. One PVC per data set is the right pattern. The team that has myapp-data (the database) and myapp-uploads (user files) as separate PVCs can back up, restore, and resize them independently.

Can multiple pods mount the same PVC?

Depends on the access mode. RWO: no. ROX: yes. RWX: yes (requires NFS or similar). The team that runs a clustered database with multiple replicas uses RWX; the team that runs a single-instance Postgres uses RWO.

How this fits the rest of the stack

For a sense of what the full project costs before it commits, the RunxBuild hosting calculator shows the line items together. The API, the database, the storage, the worker, the bandwidth - each one is a separate number, and the team’s mental model for the platform is the sum of those numbers.

Useful related references:

#kubernetes#volumes#pv#pvc#storage