Containers are ephemeral: when a pod is rescheduled, anything written inside it is gone. Databases, uploads and other state need persistent storage. Kubernetes handles this with PersistentVolumes (PV), PersistentVolumeClaims (PVC) and StorageClasses. Understanding them, and their limits on self-managed VPS clusters, is key to running stateful apps safely.

The three objects

Object Who creates it What it is
StorageClass Admin (or the distribution) A type of storage, and how to provision it (driver, parameters)
PersistentVolumeClaim (PVC) The app (you) “I need 10 Gi of ReadWriteOnce storage of class X”
PersistentVolume (PV) Created dynamically by the provisioner (or manually) The actual volume bound to the claim

With dynamic provisioning, you only write the PVC. The StorageClass’s provisioner creates the PV.

kubectl get storageclass
kubectl get pvc,pv -A

A PVC and a pod using it

apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: app-data
spec:
  accessModes: [ReadWriteOnce]
  storageClassName: local-path
  resources:
    requests:
      storage: 10Gi
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: app
spec:
  replicas: 1
  selector: { matchLabels: { app: app } }
  template:
    metadata: { labels: { app: app } }
    spec:
      containers:
        - name: app
          image: myapp:1.8.3
          volumeMounts:
            - name: data
              mountPath: /var/lib/app
      volumes:
        - name: data
          persistentVolumeClaim:
            claimName: app-data

Access modes

Mode Meaning
ReadWriteOnce (RWO) Mounted read-write by one node (most block storage)
ReadWriteOncePod Only one pod
ReadOnlyMany (ROX) Read-only by many nodes
ReadWriteMany (RWX) Read-write by many nodes (needs NFS, CephFS, or similar)

Most databases want RWO. Shared uploads across replicas need RWX, or better, object storage. See MinIO.

Storage options on VPS clusters

Without a cloud provider’s volume driver, you choose a provisioner:

Option How it works Trade-off
local-path (k3s default) Directory on the node’s disk Simple and fast, but data is tied to one node
Longhorn Replicates volumes across nodes’ disks Survives node loss; uses extra disk and network
OpenEBS, Rook-Ceph Distributed storage Powerful; Ceph is complex and resource-hungry
NFS provisioner Volumes on an NFS server RWX support; NFS server is a single point of failure

For a single-node k3s cluster on one VPS, local-path is fine. For multi-node clusters where pods may move, use Longhorn, or keep stateful services outside the cluster. See k3s and NFS server and client.

StatefulSets for databases

Databases and other stateful apps usually run as StatefulSets, which give each replica a stable name and its own PVC (via volumeClaimTemplates). Many teams run databases outside Kubernetes on a dedicated VPS, or use operators (CloudNativePG, Percona operators), which handle replication and backups.

Reclaim policy: don’t lose data by accident

kubectl get pv -o custom-columns=NAME:.metadata.name,RECLAIM:.spec.persistentVolumeReclaimPolicy
  • Delete (common default): deleting the PVC deletes the volume and its data.
  • Retain: the volume is kept for manual recovery.

Set reclaimPolicy: Retain on StorageClasses for important data, or patch existing PVs:

kubectl patch pv <pv-name> -p '{"spec":{"persistentVolumeReclaimPolicy":"Retain"}}'

Backups

Persistent volumes are not backups. Options:

  • Velero: backs up Kubernetes resources and volume data (with restic/kopia) to S3-compatible storage.
  • Application-level dumps (pg_dump, mysqldump) in a CronJob, pushed off-site.
  • Longhorn backups to S3.

See backup strategy (3-2-1) and restic.

Expanding a volume

If the StorageClass has allowVolumeExpansion: true, edit the PVC’s storage request to a larger value. Many drivers resize online.

Quick reference

Task Command / field
List classes kubectl get storageclass
Request storage PVC with accessModes, storageClassName, storage
Status kubectl get pvc,pv
Keep data on PVC delete reclaimPolicy: Retain
Databases StatefulSet / operator, or outside the cluster

Related: Kubernetes deployments and services and Kubernetes debugging pods.

  • Kubernetes
  • Storage
  • DevOps