Scenario 01 — Protect a stateful app, consistently
The everyday case. You run a database on a PVC. You want nightly backups that the database can actually be restored from, and a sane retention window. This is the usual reason to install Kopiur, and it takes four resources in one namespace.
It goes one step past example 01. It adds hooks that quiesce the database around the snapshot, so the captured bytes are application-consistent, not just crash-consistent.
The values you’ll actually change
Section titled “The values you’ll actually change”| Field | Where | What it does |
|---|---|---|
backend.s3.bucket / prefix / endpoint / region |
Repository |
Points at your object store. |
KOPIA_PASSWORD |
the Secret |
Encrypts the repository. Lose it, lose the backups. |
sources[].pvc.name |
SnapshotPolicy |
The PVC to back up. |
hooks.*.workloadExec |
SnapshotPolicy |
The commands that quiesce and unquiesce your app. Swap the Postgres ones for your database’s equivalents. |
retention.keep* |
SnapshotPolicy |
The GFS window: how many daily, weekly, and monthly snapshots to keep. |
schedule.cron / jitter |
SnapshotSchedule |
When it runs. H picks a stable minute for you. |
apiVersion: v1kind: Secretmetadata: name: postgres-repo-creds namespace: billingtype: OpaquestringData: AWS_ACCESS_KEY_ID: "REPLACE_ME" AWS_SECRET_ACCESS_KEY: "REPLACE_ME" # Lose this and the backups are unrecoverable — kopia cannot decrypt without it. KOPIA_PASSWORD: "choose-something-long-and-random"---apiVersion: kopiur.home-operations.com/v1alpha1kind: Repositorymetadata: name: postgres-primary namespace: billingspec: backend: s3: bucket: my-backups prefix: billing/postgres/ endpoint: s3.us-east-1.amazonaws.com region: us-east-1 auth: secretRef: name: postgres-repo-creds encryption: passwordSecretRef: name: postgres-repo-creds key: KOPIA_PASSWORD create: enabled: true # this is a NEW repo — initialize it on first connect---apiVersion: kopiur.home-operations.com/v1alpha1kind: SnapshotPolicymetadata: name: postgres-data namespace: billingspec: repository: name: postgres-primary # kind defaults to Repository (same namespace) sources: - pvc: name: postgres-data # ── How the volume is captured ──────────────────────────────────────────── # Snapshot takes a point-in-time CSI VolumeSnapshot, then kopia reads THAT — no # app downtime, consistent on disk. Combined with the hooks below you also get # application consistency. (copyMethod defaults to Snapshot — shown explicitly # for teaching — and needs the CSI snapshot stack + a VolumeSnapshotClass for # your driver, ideal for databases like this; Direct reads the live volume and # works on any storage, but set it explicitly if you have no CSI snapshots.) copyMethod: Snapshot # volumeSnapshotClassName: csi-hostpath-snapclass # set if not the cluster default # ── Quiesce the database around the snapshot ────────────────────────────── # Hooks run IN THE WORKLOAD (not the mover): tell Postgres a backup is starting # before the snapshot, and that it finished after. A hook failure aborts the # backup unless continueOnFailure: true. hooks: beforeSnapshot: - workloadExec: podSelector: matchLabels: app: postgres container: postgres command: ["/bin/sh", "-c", 'psql -U postgres -c "SELECT pg_backup_start(''kopiur'');"'] timeout: 2m afterSnapshot: - workloadExec: podSelector: matchLabels: app: postgres container: postgres command: ["/bin/sh", "-c", 'psql -U postgres -c "SELECT pg_backup_stop();"'] # Run even if something upstream failed, so the DB never stays in # backup mode. continueOnFailure: true # ── How long to keep snapshots (GFS — the only successful-retention driver) ── retention: keepDaily: 14 keepWeekly: 6 keepMonthly: 3---apiVersion: kopiur.home-operations.com/v1alpha1kind: SnapshotSchedulemetadata: name: postgres-data-nightly namespace: billingspec: policyRef: name: postgres-data schedule: # Jenkins-style H pins a deterministic per-schedule minute; jitter spreads # load so many schedules don't stampede the repository at once. cron: "H 2 * * *" jitter: 30m runOnCreate: false # GitOps-friendly: don't fire the instant it's appliedVerify it worked
Section titled “Verify it worked”The Repository should reach Ready first:
$ kubectl get repository -n billingNAME PHASE AGEpostgres-primary Ready 30sThen fire one backup by hand before you trust the schedule. That confirms the whole chain: recipe, then mover, then snapshot. This Snapshot manifest ships with the example, in its manual-snapshot section:
# Verification: fire ONE backup by hand to confirm the whole chain (recipe →# mover → snapshot) before trusting the nightly schedule. generateName lets you# re-apply this repeatedly to take another test snapshot.apiVersion: kopiur.home-operations.com/v1alpha1kind: Snapshotmetadata: generateName: postgres-data-test- namespace: billingspec: policyRef: name: postgres-data$ kubectl apply -f deploy/examples/scenarios/01-protect-stateful-app.yaml
$ kubectl get snapshots -n billing -wNAME PHASE ORIGIN SNAPSHOT AGEpostgres-data-test-x9f Running manual 7spostgres-data-test-x9f Succeeded manual k1f1ec0a8 44sA SNAPSHOT id on a Succeeded backup means the data is in the repository. From here the SnapshotSchedule takes over nightly.
See also
Section titled “See also”- Backups & schedules: every field on these three resources, including the other hook forms (
runJob,httpRequest) andcopyMethod. - Scenario 06, verification drills: prove these backups actually restore.
- Movers, RBAC & credentials: where the mover runs and what it needs.