Content Management that can withstand the plundering hoards

Back up and restore a website

Arrange scheduled backups and restores with your storage platform. Public release rollback restores generated output only; it cannot recover editable drafts. Choose backup frequency and retention according to how much work you can afford to lose, and test recovery before depending on it.

What a complete backup contains

Item Location Why retain it
Private volume <site>-private, mounted at /cms Page source, settings, uploaded bytes/metadata, custom themes and installed renderer/block packages
Published volume <site>-published, mounted at /www Active and retained releases, current symlink, release sequence and activation records
Website manifest Kubernetes Website spec/metadata Hostnames, storage classes/sizes, ingress, retention and authentication reference
Authentication and TLS configuration Referenced Secrets and certificate/issuer configuration Sign-in/session keys and trusted endpoints; certificates may instead be reissued
Identity configuration IdP records; retained Ipseity client/membership ConfigMaps and namespace labels when applicable Accounts, client audiences, roles and administrator access
Software versions Controller/frontend/CMS image digests and CRD/RBAC revision Reproduce the code/schema that wrote the data

Back up the whole private and published volume, including dotfiles, symlinks, ownership and permissions. See storage layout for individual files. Never copy only current or only rendered HTML and expect an editable project. An asset can appear in multiple releases; filesystem hard-link preservation is useful, but preserving all bytes and symlinks is required.

Store credentials and private source in encrypted, access-controlled backup storage. Kubernetes Secret YAML is base64-encoded, not encrypted merely because it is a Secret. Record a backup identifier, time, site namespace/name, active release and image digests with the backup.

Take a consistent backup

A copy while publishing or editing may mix different revisions. Use a storage system that supports a coordinated snapshot of both volumes, or pause all writers for the entire copy. A single-volume crash-consistent snapshot is not automatically a matching two-volume backup. Follow the storage vendor's snapshot/export procedure; there is no portable kubectl command that creates usable snapshots on every driver.

For a maintenance-window copy using the portable deployment:

  1. Announce an editing pause. Finish or cancel any publish/import and record the active release from the CMS status. Capture Website manifests, software revisions and credentials securely.
  2. Record the current controller replica count. Scale the controller Deployment to zero so reconciliation cannot undo a temporary CMS scale-down. This pauses reconciliation for all Websites managed by that controller; existing public frontend pods continue serving. Pause external GitOps/Orbit automation that would immediately restore these replica counts, following its own procedure.
  3. Scale the target CMS Deployment to zero and wait until its pod is gone. Stop auxiliary import/backup jobs or other processes that write these volumes. Do not use cms.enabled: false as a backup pause: that changes desired resources.
  4. Snapshot or copy both PVCs with the storage platform's supported tooling. For a file archive, mount them in a dedicated maintenance Job and use a tool that preserves symlinks, modes and all files. Scratch application containers have no shell, tar or backup utility. Unmount/stop the maintenance writer before restarting the CMS.
  5. Restore the controller's recorded replica count. Reconciliation brings the enabled CMS back to its single replica. Restore paused external automation.
  6. Check CMS readiness, sign-in, current release and public content; record the backup's completion and verify the remote copy/checksums.

Example scale commands for the default controller and the installation guide's site:

kubectl get deployment viking-cms-controller -n viking-cms -o jsonpath='{.spec.replicas}'
kubectl scale deployment/viking-cms-controller -n viking-cms --replicas=0
kubectl scale deployment/example-cms -n websites-example --replicas=0
kubectl get pods -n websites-example

The last command is a check, not a wait: confirm the CMS pod has actually terminated before copying. Restore the recorded controller replica count after the backup; do not leave reconciliation paused. Provider-specific backup Jobs, snapshot classes and remote credentials must be prepared for your cluster.

Restore into an isolated environment first

  1. Provision compatible Kubernetes/storage/ingress and the recorded CRD/RBAC and component images. Keep the test site away from production DNS and writers.
  2. Restore both volumes from the same backup set. Prepare PVCs in the intended namespace named <site>-private and <site>-published, with suitable capacity and RWX access. Check existing claim ownership against controller adoption rules before reconciling; do not blindly reuse claims owned by another Website.
  3. Restore private file ownership for UID/GID 65532. The operator's volume-permissions init container recursively chowns both roots at startup; the storage driver/export must permit this. Inspect its logs if it fails.
  4. Restore the authentication Secret and provider-side client/roles. For a test hostname, register its callback and update the test Website/auth settings. Automatic Ipseity client ConfigMaps record the creating Website UID; a recreated Website has a new UID. Review retained identities with Ipseity recovery rules before enabling automatic provisioning; do not overwrite grants indiscriminately.
  5. Apply the Website only when restored storage and credentials are ready. Keep exactly one CMS writer. Confirm current resolves to a complete releases/<id> directory with release.json and that the sequence/history files were restored, rather than inventing new values.
  6. Check admin readiness, sign-in, page editing, media, plugins, theme preview and public files. On the isolated copy, save and publish a test change, then verify that a retained-release rollback works if configured.
  7. Compare page/asset counts, sample file checksums and release IDs with backup records. Record recovery time and any loss of work before planning a production cutover.

For an in-place restore, pause the controller, all CMS writers and external reconcilers as above, retain the failed/current volumes as a separate recovery point, restore the matched backup pair, then resume and verify. Frontends can cache the resolved root for the configured freshness interval; avoid serving a partially copied filesystem by stopping/rerouting frontend traffic during an in-place file restore. A snapshot-volume cutover also needs storage-specific mount/restart handling.

If only public files survive, serve that static release while recovering source from another backup. Generated HTML is not the authoritative editor format.