Upgrades
Use this procedure for an administrator-managed deployment. Run commands from repository root after selecting the intended cluster context. Start with a verified backup and record existing image references, Website manifests, installed CRD/RBAC and active release IDs. Kubernetes workload rollback alone does not restore a changed schema or writable data.
Upgrade procedure
- Review the release notes and settings reference, and test the target version on an isolated restored site. Check legacy authentication, admission defaults, ingress fields and automatic Ipseity behaviour described below.
- Update the canonical/portable CRD and RBAC copies in the deployment source. For Orbit, synchronize its separate copies too; the script below handles the CRD only, so review RBAC separately.
- Apply the new CRD and required RBAC before rolling the controller. For the
portable install, use
kubectl apply -f deploy/01-crd.yamlandkubectl apply -f deploy/02-operator-rbac.yamlwith reviewed manifests. - Set the controller image and its
--frontend-image/--cms-imagearguments to the intended compatible versions, then deploy the updated operator manifest. These settings affect existing Websites managed by that operator. Reusing the same image string does not force pod recreation just because it was repushed. - Check
kubectl rollout status deployment/viking-cms-controller -n viking-cms, Website conditions/generation, site Deployment rollouts, CMS sign-in/readiness, public responses and the running Server/build versions. The CMS uses Recreate, so expect an editing interruption; frontend uses rolling updates. - Reapply intended Website fields that an older schema pruned. Confirm stored
fields with
kubectl get website NAME -n NAMESPACE -o yaml. Do a controlled save/preview/publish and a retained-release rollback on a test site before considering the upgrade verified.
If verification fails, inspect component logs/conditions before more mutation. Roll back image references only if the old binary is compatible with current schema/data. Otherwise follow the prepared matched-volume/configuration restore procedure. Do not delete the CRD or PVCs as an upgrade troubleshooting step.
Data-preservation requirements
Upgrades must preserve:
- Website specifications,
- published content,
- private CMS content,
- existing releases subject to the retention policy,
- credentials and Secret references.
Operator upgrades
Upgrade the operator image in deploy/03-operator.yaml (or the Orbit Deploy
image mapping) and roll the Deployment. Reconciliation is idempotent: applying
the new operator re-reconciles existing Website resources against their
declared state without destructive changes to workloads or storage.
CRD schema evolution
Review compatibility and migration instructions before applying an updated CRD.
cms.authentication is a flattened reference: it must contain a secretRef
with a non-empty name when supplied. Omitted CMS hostnames now default to
cms.<domain> and omitted authentication can use automatic Ipseity integration. Upgrading to the flattened schema (from the older
oauth2/basic mechanism nodes) rejects Website objects with an empty
authentication: {} and any object still using authentication.oauth2 /
authentication.basic at the API level. Existing Website objects must be
migrated to cms.authentication.secretRef.name (and the referenced Secret
converted to the new key format — see Authentication)
before applying the new CRD. Similarly, frontend.replicas has no schema
default in the CRD — the operator defaults a nil value to 2; an explicit 0
is scale-to-zero, so a previously-defaulted 2 remains 2.
When upgrading from older implementations, check these compatibility changes:
- CMS hostname/authentication are no longer mutually required by admission. Empty explicit authentication references remain invalid; the controller checks availability of explicit or automatic authentication before exposing a CMS.
- The operator rejects a same-Website conflict: the CMS hostname must not
equal any public hostname of the same
Website(two Ingress rules for the same hostname would collide). Subdomain relationships are permitted because ingress hostname matching is exact, socms.example.comunderdomain: example.comis now accepted. - The CMS auth Secret gained an optional
publishRoleskey (gates publish/rollback) andcmsHostname(pin the external hostname); IdP endpoints must be HTTPS (loopback-onlyhttp://requires--allow-insecure-auth-http); the fixed-CSRF-token mode was removed and logout isPOST /oauth2/logout(CSRF-protected; aGETreturns405). Browser session cookies are now AES-256-GCM encrypted (opaque), so upgrading invalidates outstanding sessions. deploy/examples/06-cms-oauth2-secret.yamlships an invalid-base64 shortjwtSecretplaceholder, so operator validation blocks CMS provisioning even though Kubernetes can store that Secret. Generate a real key withhead -c 32 /dev/urandom | base64 -w0.
Use the portable deploy/01-crd.yaml from the target release alongside compatible
controller, CMS and frontend images.
Ingress TLS and cert-manager options
Upgrading the controller image alone does not update the CRD installed by
Orbit Deploy. The Orbit deployment repository has its own copy at
default/01-crd.yaml; it must include spec.ingress.tls, issuer and
cluster_issuer. An older schema prunes these unknown fields before storing
the Website, so the operator sees default TLS and no issuer selection.
Before deploying a schema change, synchronise and review the local Orbit copy:
scripts/sync-orbit-crd.sh /path/to/orbit-deployments/viking-cms
scripts/sync-orbit-crd.sh --check /path/to/orbit-deployments/viking-cms
The script defaults to this checkout's orbit-deployments link. It only edits
the local CRD manifest; committing and deploying remain separate actions.
Deploy the corrected CRD through Orbit Deploy before reapplying affected Website specifications. Fields already pruned from stored Websites cannot be recovered by upgrading the schema; restore the intended values from the original Website manifest. For example, plain HTTP uses:
spec:
ingress:
className: tak-os
tls: false
TLS with a cluster-scoped certificate issuer instead uses:
spec:
ingress:
className: tak-os
tls: true
cluster_issuer: letsencrypt-prod
For a namespaced Issuer, use issuer instead of cluster_issuer. These are
Website fields, not controller command-line flags. After updating the Website,
check its stored spec.ingress and both generated Ingresses; the operator
reconciles TLS and issuer changes without an operator restart. The CMS itself
still requires HTTPS for its production OAuth2 configuration; turning off
ingress TLS does not change its authentication or secure-cookie settings.
Data retention
PVC objects are retained when a Website is deleted; they must be
removed manually. This is separate from the PV/storage class reclaim policy:
deleting a PVC can still delete its actual backing volume. Rollback history and releases subject to retention are not a
replacement for storage backups — snapshot the PVCs before major upgrades.
Automatic Ipseity provisioning
Apply the new CRD and RBAC before rolling out the controller. This adds
spec.description and spec.cms.enabled, relaxes authentication/hostname
admission rules for automatic provisioning, and grants namespace label patches
and writes to retained Ipseity ConfigMaps and owned authentication Secrets.
Both the portable and Orbit CRD/RBAC copies must be updated. No changes to
existing explicit authentication references are required. Review Websites with
no CMS configuration: when Ipseity is discovered these now receive an
authenticated CMS unless spec.cms.enabled: false is set. See
Ipseity authentication for discovery, initial administrator,
namespace isolation, and retained identity records.
Verify the installed schema
Confirm that the installed CRD matches the reviewed release manifest before
changing Website settings. After applying it, inspect the stored Website with
kubectl get website NAME -n NAMESPACE -o yaml and compare the fields you set.
Follow the troubleshooting steps
if expected fields are absent, then reapply the original Website manifest once
the corrected schema is installed.
