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:
- 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.
- 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.
- 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: falseas a backup pause: that changes desired resources. - 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,
taror backup utility. Unmount/stop the maintenance writer before restarting the CMS. - Restore the controller's recorded replica count. Reconciliation brings the enabled CMS back to its single replica. Restore paused external automation.
- 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
- Provision compatible Kubernetes/storage/ingress and the recorded CRD/RBAC and component images. Keep the test site away from production DNS and writers.
- Restore both volumes from the same backup set. Prepare PVCs in the intended
namespace named
<site>-privateand<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. - Restore private file ownership for UID/GID 65532. The operator's
volume-permissionsinit container recursively chowns both roots at startup; the storage driver/export must permit this. Inspect its logs if it fails. - 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.
- Apply the Website only when restored storage and credentials are ready.
Keep exactly one CMS writer. Confirm
currentresolves to a completereleases/<id>directory withrelease.jsonand that the sequence/history files were restored, rather than inventing new values. - 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.
- 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.
