Docs / Backup & Recovery / Back Up Kubernetes Persistent Volumes

Back Up Kubernetes Persistent Volumes

By Admin · Mar 15, 2026 · Updated Apr 23, 2026 · 311 views · 3 min read

Why Back Up Persistent Volumes?

Kubernetes persistent volumes (PVs) store stateful data for databases, file uploads, and application state. Unlike stateless pods that can be recreated from images, losing a persistent volume means losing irreplaceable data. This guide covers multiple strategies for backing up PVs reliably.

Backup Strategy Overview

  • Velero + Kopia — Full cluster backup including PVs for disaster recovery
  • CSI Volume Snapshots — Storage-level snapshots, fastest but provider-dependent
  • Application-level dumps — Database exports via CronJobs

Installing Velero

wget https://github.com/vmware-tanzu/velero/releases/download/v1.14.0/velero-v1.14.0-linux-amd64.tar.gz
tar xzf velero-v1.14.0-linux-amd64.tar.gz
sudo mv velero-v1.14.0-linux-amd64/velero /usr/local/bin/

cat > /tmp/velero-credentials << EOF
[default]
aws_access_key_id=YOUR_ACCESS_KEY
aws_secret_access_key=YOUR_SECRET_KEY
EOF

velero install \
    --provider aws \
    --plugins velero/velero-plugin-for-aws:v1.10.0 \
    --bucket velero-backups \
    --secret-file /tmp/velero-credentials \
    --backup-location-config region=us-east-1,s3Url=https://s3.wasabisys.com \
    --use-node-agent \
    --default-volumes-to-fs-backup \
    --uploader-type=kopia

Backing Up with Velero

# Backup namespace with PVs
velero backup create my-app-backup \
    --include-namespaces production \
    --default-volumes-to-fs-backup --wait

# Schedule daily backups
velero schedule create daily-production \
    --schedule="0 2 * * *" \
    --include-namespaces production \
    --default-volumes-to-fs-backup --ttl 720h

Pod Annotations for Volume Selection

apiVersion: apps/v1
kind: Deployment
metadata:
  name: my-app
spec:
  template:
    metadata:
      annotations:
        backup.velero.io/backup-volumes: data,config
    spec:
      containers:
      - name: app
        volumeMounts:
        - name: data
          mountPath: /data
      volumes:
      - name: data
        persistentVolumeClaim:
          claimName: app-data-pvc

CSI Volume Snapshots

apiVersion: snapshot.storage.k8s.io/v1
kind: VolumeSnapshotClass
metadata:
  name: csi-snapclass
driver: your-csi-driver.example.com
deletionPolicy: Retain
---
apiVersion: snapshot.storage.k8s.io/v1
kind: VolumeSnapshot
metadata:
  name: db-snapshot-20260315
  namespace: production
spec:
  volumeSnapshotClassName: csi-snapclass
  source:
    persistentVolumeClaimName: postgres-data-pvc

Database CronJob Backup

apiVersion: batch/v1
kind: CronJob
metadata:
  name: postgres-backup
  namespace: production
spec:
  schedule: "0 1 * * *"
  jobTemplate:
    spec:
      template:
        spec:
          containers:
          - name: backup
            image: postgres:16
            env:
            - name: PGPASSWORD
              valueFrom:
                secretKeyRef:
                  name: postgres-secret
                  key: password
            command:
            - /bin/bash
            - -c
            - |
              pg_dump -h postgres-svc -U app_user app_db | \
                gzip > /backup/db-$(date +%Y%m%d).sql.gz
            volumeMounts:
            - name: backup-vol
              mountPath: /backup
          volumes:
          - name: backup-vol
            emptyDir:
              sizeLimit: 10Gi
          restartPolicy: OnFailure

Restoring from Velero

velero get backups
velero restore create --from-backup my-app-backup --wait
velero restore create --from-backup my-app-backup \
    --namespace-mappings production:staging --wait

Best Practices

  • Use Velero for full cluster DR and CSI snapshots for rapid PV rollbacks
  • Always do application-level backups for databases alongside volume backups
  • Test restores monthly in a separate namespace
  • Use backup TTLs to auto-clean old backups
  • Store backups in a different region than your cluster
  • Encrypt backup data at rest

Was this article helpful?