Single PVC
Restore one PersistentVolumeClaim's data from the NFS restic repo and rebind it to the target app.
Use this when one application's data is corrupt or accidentally wiped, but the cluster and other apps are fine. Common cases: Keycloak themes overwritten, MinIO bucket data deleted, a CMS upload directory wiped.
How the mapping works
Each PVC's data lives in a per-volume subdirectory under the NFS export (e.g. /srv/nfs/openg2p/<namespace>-<pvc>-<pv-uuid>/ on the nfs-csi StorageClass, or a native NFS PV path). On its own restic just sees opaque directory names. The sidecar manifest at /var/lib/openg2p-backup/nfs/.pvc-mapping.yaml joins each directory against kubectl get pv (including CSI volumeAttributes.subdir) so restore knows which app a path belongs to. This file is regenerated every NFS run — always inspect the latest version.
Step 1 — Find the PVC
ssh ubuntu@<backup-host> sudo cat /var/lib/openg2p-backup/nfs/.pvc-mapping.yaml | jq '.[] | select(.pvc_namespace == "keycloak")'You'll see entries like:
- nfs_path: pvc-abc123-def456
pv_name: pvc-abc123-def456
pvc_namespace: keycloak
pvc_name: keycloak-data
pvc_size: 20Gi
storage_class: nfs-csi
app_label: app=keycloak
backed_up_at: 2026-04-27T03:30:11ZStep 2 — Dry-run
./openg2p-backup.sh restore \
--config backup-config.yaml \
--component nfs \
--target keycloak/keycloak-data \
--dry-runConfirms the restic source and the Bound PV destination on storage it would push to.
Step 3 — Restore (restic → Bound path on storage)
Scale the app down first, then:
This:
Resolves the live Bound PV
subDir(or native NFS basename) fornamespace/pvc.restic restore … --tag nfson the backup host (fails if empty).Pushes the restored tree over SSH onto
${nfs.export_root}/<Bound-subdir>on storage (moves any existing contents aside to.precrash). Bitnami MinIO dirs getchown 1001:1001.
After a full rebuild (DR)
Post-rebuild NFS cron may have already taken a new (empty) --tag nfs snapshot. Pin a pre-disaster snapshot:
The push still targets the Bound (new) UUID, even when restic contains the old UUID path.
Step 4 — Push the data to the live NFS export (manual fallback)
Only needed if the orchestrator SSH push failed. The NFS export is mounted read-only on the backup host.
Plan A — replace the whole PVC's contents
Plan B — selective file restore
Step 5 — Verify
Restoring a deleted PVC (PV/PVC objects gone)
If the PVC itself was deleted (not just the data):
Check
.pvc-mapping.yamlfor the PV/PVC names + size + storage class.Restore Kubernetes objects via a rancher-backup
RestoreCR — see full-rebuild.md. The orchestrator always restores cluster-wide (--target clusterdoes not filter by namespace). For a single namespace, apply a manualRestoreCR with rancher-backup's restore filters.The restored PVC will bind to a freshly-provisioned PV (because the old PV is also gone). The new PV's
nfs.pathwon't match the original. Two options:Move the restic-restored data into the new PV's NFS path.
Edit the restored PV manifest (before applying) to point at the original NFS path, so it binds to the existing data.
The second is faster but requires manual YAML edits. Operate carefully.
Common gotchas
Permissions — NFS exports often run with
root_squash. Restored files may show up asnobody:nogroup(ornobody:1001). Match what the workload expects: Bitnami MinIO needschown -R 1001:1001on the PVC dir or the pod fails withPermission deniedon.root_user.Bound UUID ≠ restored UUID after full rebuild — helmfile creates new
nfs-csiPVs; restic restores old UUID dirs. Put restored data under the Bound PV’ssubDir(seekubectl get pv … volumeAttributes.subDir). Leaving data only under the old UUID leaves pods empty/failing.CSI volume source is immutable — you cannot
kubectl patch pvto changespec.csi.volumeAttributes.server/subDir, and you cannot addspec.nfsonto a CSI PV. Recreate the PV or (preferred) copy data into the Bound path.mvinto a non-empty directory —mv chunks dest/chunksfails withDirectory not empty.mvhas no-r. Replace the whole Bound directory, orrm -rf dest/*thenrsync -a src/ dest/.Destination must exist before
tar -C— create withmkdir -pon storage or extract fails withCannot open: No such file or directory.Same UUID on live NFS and in staging — after rancher restore of native NFS PVs, staging nests data under
/tmp/openg2p-nfs-restore/<ns>-<pvc>-<ts>/<nfs-path>/. Copy/move that inner directory, not the outer timestamped folder.Trailing slashes in
tar -C— the path is the destination directory. Always verify withdu -sh/lsafter the copy.Forgetting to scale the app down — you can corrupt new files mid-restore. Always pause the consuming Deployment/StatefulSet first.
The restored data is older — by definition. Anything written after the last NFS backup snapshot is gone. Check the restored snapshot timestamp in
.pvc-mapping.yaml(backed_up_at).Keycloak / Superset still CrashLoop after PG cutover — on a storage rebuild they often still point at the old Postgres private IP in ConfigMaps/Secrets. Update those to the new storage IP, then restart — see full-rebuild.md Step 9.
Last updated
Was this helpful?