OpenClaw EnterpriseDOCSGitHub

Plan an OpenClaw Enterprise upgrade

Use this checklist before changing an OpenClaw Enterprise (OCE) controller, runtime, Helm chart, or Installation configuration in an environment that must retain data. It inventories the state an image update does not change by itself.

The checklist complements the production image upgrade procedure and the persistent local k3d procedure. A custom retained Compose or Compose-and-k3d environment has no supported in-place upgrade command; use this page as its migration inventory.

Classify the release

Check operator access

Use the upgrade's OCC CLI, endpoint, CA and protected key file. Check the issuance record's expiry, if present, against the planned upgrade and recovery window. Before freezing writers, verify the Installation ID and fleet access. Set BASELINE to the intended release's baseline directory.

bash
(  set -euo pipefail  installation_id=$(jq -er '.installationId | strings | select(length > 0)' \    "${BASELINE:?}/installation-secret.json")  /secure/occ/bin/occ --output json installation get |    jq -es --arg id "$installation_id" 'length == 1 and .[0].id == $id' >/dev/null  /secure/occ/bin/occ --output json installation deployment-inventory |    jq -es --arg id "$installation_id" \      'length == 1 and .[0].installationId == $id and (.[0].namespaces | type == "array")' >/dev/null)

Continue on exit zero, including for an empty fleet. If OCC rejects the key, obtain a current key from the Installation administrator and repeat both reads. For permission failures, check the inventory grants. Resolve target or connection errors before maintenance. The helper rechecks the inventory before stopping writers.

Check ReplicaSet capacity

Plan capacity before Helm or stopping workloads. For planning or a stalled rollout, use Bash with the release's kubeconfig, context and namespace. Set the affected Deployment name.

bash
kube=(kubectl --kubeconfig /secure/occ/kubeconfig --context '<reviewed-context>'  --namespace '<release-namespace>')deployment=openclaw-enterprise-api # repeat for openclaw-enterprise-worker"${kube[@]}" get resourcequota -o yaml"${kube[@]}" get replicasets"${kube[@]}" describe deployment "$deployment""${kube[@]}" get events --field-selector "involvedObject.kind=Deployment,involvedObject.name=$deployment" \  --sort-by=.metadata.creationTimestamp

These commands are read-only. A condition or event naming the affected Deployment and reporting exceeded quota for count/replicasets.apps during this rollout identifies the cause. A full quota alone does not. Reason names vary, and events expire.

For count/replicasets.apps, inspect each quota's spec.hard, status.hard and status.used. A limit is absent only after a successful full list shows none configured for this resource. Capacity is unknown if reads fail or an applicable status is missing or has not caught up with the configured limit. Ask the namespace owner to resolve uncertainty without adding privileges. Compare each limit independently against namespace usage plus needed slots. Room under these limits does not guarantee other admission checks will pass.

The unfiltered ReplicaSet list includes current sets, retained history and other releases, even with zero replicas. Before Helm, compare each candidate Pod template rendered with the target values against the Deployment's owned sets. Allow for target admission defaults and mutations. Reserve a slot for each concurrent rollout unless its resulting template is known to match an owned set. If admission is uncertain, reserve the slot. The live Deployment still describes the old release and cannot establish a match for the candidate.

After Helm submits the candidate, inspect the live spec.template and revisionHistoryLimit with "${kube[@]}" get deployment "$deployment" -o yaml. Compare the template with "${kube[@]}" get replicaset '<set-name>' -o yaml. Check owner UIDs and ignore the generated pod-template-hash label. Scaling or a generation change alone does not require a new set. Preserve history; do not count on cleanup before creation.

If room is insufficient, ask the namespace owner for object capacity. After recovery, confirm rollout completion, Ready Pods and expected running imageID values when checking manually. Do not replay Helm or delete history or data. Follow upgrade recovery and preserve any existing helper or rollback owner's control.

Check Job object capacity

For releases that create Jobs, check capacity before Helm. Reuse the Bash kube array above with the reviewed context and each namespace that receives Jobs. Use these read-only commands.

bash
"${kube[@]}" get resourcequota -o yaml"${kube[@]}" get jobs.batch"${kube[@]}" get events --sort-by=.metadata.creationTimestamp

Apply the quota rules above for failed reads, absent limits, missing or stale status, and multiple quotas. For each applicable count/jobs.batch quota, check spec.hard, status.hard and status.used. Add the release's peak number of additional Job objects that can exist at once to status.used. The sum must fit within status.hard. Existing usage includes retained completed and failed Jobs. Include new Jobs that remain after completion or failure in the peak. Plan without relying on cleanup before creation. For example, a quota with a hard limit of 12 and a used count of 9 leaves room for 3 Jobs. A release that needs 4 additional Jobs at once exceeds that limit. Ask the namespace owner about insufficient capacity. See Kubernetes object count quota.

A successful server dry run of one request reserves no capacity. Diagnose a quota refusal from the Kubernetes API's rejection of the Job creation request naming count/jobs.batch, or a corresponding controller event recording that rejection. Observing a full quota alone does not prove a creation request was refused. Events can expire, and other admission checks can still reject creation.

Record the starting state

Create a private evidence directory and record these values before mutation. The pre-upgrade baseline gives commands for many:

Assign every surface a disposition

Give every surface the disposition and operator action listed in upgrade surface dispositions, from controller images and database migrations to retained volumes and local profile state.

Apply the release in dependency order

  1. Install cluster prerequisites (on two clusters, upgrade the execution chart first) and reconcile protected inputs without replacing retained data.
  2. Run the upgrade preflight, including the candidate's canonical migration check. Stop on unsupported history or an unresolved required quiescence step.
  3. Upgrade the controller, worker, and Console. Wait for database migration, bootstrap, authenticated API recovery, and worker readiness. The old API Pod stops first and finishes admitted requests within its 30-second grace (shutdown timing).
  4. Reconcile persisted resources that startup intentionally preserves, including same-name Presets. Compare complete objects, not only counts or names.
  5. Update runtime image selection only when required. Reload API and worker, then deploy the recorded running fleet through OCC.
  6. Reconcile external integrations and cluster-owned supporting services.
  7. Run the acceptance checks below before ending the interruption window.

Verify the retained installation

Stop and recover safely

Stop before mutation when the source or image identity is unknown, protected and live configuration differ unexpectedly, migration history is unsupported, required backups are missing, deployment work is active, or a cluster-specific override has no reviewed candidate equivalent.

After a controller failure, inspect migration and bootstrap results before considering rollback. Do not run an older controller against state it cannot read. After a partial runtime failure, keep the healthy control plane and recover the affected Agents individually. Never delete persistent resources to make the upgrade appear clean.

If the candidate accepted writes, preserve them before restoring older data and reconcile them before reopening traffic, or repair the candidate in place.

Search documentation