kopiur trial — replacing pvc-plumber (complete, now production)¶
Complete. PR #1489 merged 2026-06-26; kopiur fully replaced the retired pvc-plumber + VolSync stack on 2026-06-27 and is the production backup system. Retained as the trial/decision record — it originally stood kopiur up alongside pvc-plumber and converted one canary app (karakeep) onto explicit kopiur CRs.
Strategic companion (fit verdict, kopiur facts, config shapes):
domains/storage/kopiur-evaluation.md.
The canary: karakeep¶
The DR-doc worked example — two RWO PVCs (data-pvc = SQLite bookmarks + assets,
meilisearch-pvc = search index). Non-critical, easy to drill.
What the cutover changed¶
| Area | old (pvc-plumber) | kopiur |
|---|---|---|
| Per-PVC config | 3 labels + dataSourceRef → ReplicationDestination |
explicit SnapshotPolicy + SnapshotSchedule + Restore, dataSourceRef → Restore |
| Operator | infrastructure/controllers/pvc-plumber/ (Wave 2) |
infrastructure/controllers/kopiur-operator/ Kustomize helmCharts: (OCI chart, Wave 2) |
| Repo config | volsync-kopia-repository ClusterES |
infrastructure/controllers/kopiur/ (ns + ESO + ClusterRepository, Wave 3) |
| kopia repo | s3://volsync-kopia/cluster |
s3://kopiur/ (dedicated, isolated bucket) |
| Namespace gate | pvc-plumber.io/managed-namespace |
allowedNamespaces on the ClusterRepository |
Deploy is pure GitOps (merge to main, ArgoCD reconciles — no kubectl apply):
- Wave 2 kopiur-operator — renders the OCI chart oci://ghcr.io/home-operations/charts/kopiur
(version pinned in infrastructure/controllers/kopiur-operator/kustomization.yaml),
installs the CRDs + operator + webhook.
ServerSideApply=true avoids the 256KB last-applied-config limit on CRDs.
- Wave 3 kopiur-config — namespace + ESO + ClusterRepository.
- Wave 6 the my-apps AppSet renders the app with the kopiur CR bundle and the
repointed PVCs.
Watch:
kubectl get applications -n argocd | grep kopiur
kubectl -n kopiur-system get pods,clusterrepository
kubectl -n karakeep get snapshotpolicy,snapshotschedule,snapshot,restore
The restore-before-bind drill¶
dataSourceRef is immutable, so a live PVC still points at its original source;
the populator only proves out on recreate:
- Confirm at least one kopiur
Snapshotforkarakeep/data-pvcexists in the repo. - Scale karakeep web to 0 → delete
data-pvc→ let ArgoCD recreate it. - Expect: the PVC sits Pending while
Restore/data-pvc-restorepopulates it, then binds with data; karakeep starts on the real SQLite (bookmarks intact). ThatPending-until-restored is the whole point. - ArgoCD's
/spec/dataSourceRefdiff is masked by the per-PVCServerSideApply=falseannotation + the my-apps AppSetignoreDifferences(handles the immutable-field compare).
Notes carried forward¶
- Fresh repo. kopiur reads a new kopia repo and does not see old VolSync snapshots; a repo protects data only from its first kopiur snapshot forward.
- Per-PVC YAML (~30 lines of
SnapshotPolicy+Schedule+Restore) is wrapped in themy-apps/common/kopiur-backupKustomize component to stay DRY. - Backend-down safety is kopiur's repo health-probe / preflight, not the old
wait-for-rustfsMAP.