OpenClaw EnterpriseDOCSGitHub

Deliver images to a private registry

Select a controller, runtime, and Helm chart from a verified source revision, then make the images available to every eligible Kubernetes node. This guide copies an already published image pair from private GHCR to private Amazon Elastic Container Registry (ECR) without rebuilding it. Use the production installation guide to configure and install the resulting references. Operators can instead build their own images from the selected clean source; those builds have their own digests and need independent verification.

Select a release and chart

From a successful container publication, record its source commit, successful CI run, publication run and attempt, and both immutable image digests. Check the publication receipt and remote digests; a tag alone does not establish the image contents. Publication smoke checks do not prove a production deployment. Select images that include the features you need and support the target node architectures.

For the current production example, select a controller supporting the curated Codex catalog (catalogSource: openai-curated) and the browser request origin check. The historical controller in Use published images predates the origin check and is not suitable for the current example, even when its digest and source chart match.

Use a clean checkout at that exact commit for the Helm chart, Installation examples, and helpers. From the repository root, compare git rev-parse HEAD with the recorded full source SHA and check git status --short for changes. The images listed in the installation guide were built from an older commit; they are not evidence for a chart from a newer checkout. If a separately published chart is available, verify its publication receipt, source revision, chart digest, and image annotations against the selected image publication; use it only when the release owner confirms compatibility. Chart publication is a separate process and is not assumed to have occurred for an image release.

Copy the verified images to ECR

The operator needs Skopeo, AWS CLI, access to both private GHCR packages, ECR push and read access, existing ECR repositories, and network access to both registries. GHCR requires a GitHub personal access token (classic) with read:packages and any required organization SSO authorization. Verify that both ECR repositories enforce immutable tags, with no exclusions for the chosen tags, and restrict other writers. Choose a unique tag for this publication; never replace an existing tag. See ECR tag immutability. The ECR account and Region must be the ones approved for the cluster.

Set the values from the publication record and the target registry. The source references must be the published index digests, not architecture-specific child manifests. Replace all placeholders:

bash
export AWS_REGION='<aws-region>'export ECR_REGISTRY='<aws-account-id>.dkr.ecr.<aws-region>.amazonaws.com'export SOURCE_CONTROLLER='ghcr.io/openclaw/openclaw-enterprise-controller@sha256:<64-hex-digest>'export SOURCE_RUNTIME='ghcr.io/openclaw/openclaw-enterprise-runtime@sha256:<64-hex-digest>'export ECR_CONTROLLER="$ECR_REGISTRY/openclaw-enterprise/controller"export ECR_RUNTIME="$ECR_REGISTRY/openclaw-enterprise/runtime"export ECR_TAG='<unique-release-tag>'

Start a dedicated Bash shell for registry authentication and copying. Its cleanup trap removes the credential file when the shell exits or is interrupted; do not run this in a shell with other work or traps:

bash
bash

In that shell, create the private auth file, register cleanup immediately, and authenticate. If an operation fails unexpectedly, exit this shell before investigating or retrying; start a fresh one and authenticate again. The tag lookup below can return the expected ImageNotFoundException.

bash
set -o pipefailumask 077export REGISTRY_AUTH="$(mktemp "${TMPDIR:-/tmp}/oce-registry-auth.XXXXXXXX")"trap 'rm -f -- "$REGISTRY_AUTH"' EXITtrap 'exit 130' INTtrap 'exit 143' TERMprintf '{"auths":{}}\n' > "$REGISTRY_AUTH"skopeo login --authfile "$REGISTRY_AUTH" --username '<github-username>' ghcr.ioaws ecr get-login-password --region "$AWS_REGION" | \  skopeo login --authfile "$REGISTRY_AUTH" --username AWS --password-stdin "$ECR_REGISTRY"

Enter the GitHub token at the prompt. Run aws sts get-caller-identity and confirm the selected account before writing to ECR. The temporary auth file contains registry credentials. The ECR login expires; renew it before retrying if necessary. Before copying, inspect each target tag using aws ecr describe-images in the approved account and Region. An ImageNotFoundException establishes that the tag is absent; permission, network, and other errors do not. For each repository, read its policy and the exact target tag, substituting the repository name:

bash
aws ecr describe-repositories --region "$AWS_REGION" \  --repository-names openclaw-enterprise/controller openclaw-enterprise/runtime \  --query 'repositories[].{name:repositoryName,mutability:imageTagMutability,exclusions:imageTagMutabilityExclusionFilters}'aws ecr describe-images --region "$AWS_REGION" \  --repository-name openclaw-enterprise/controller --image-ids "imageTag=$ECR_TAG" \  --query 'imageDetails[0].imageDigest' --output text

Repeat the tag lookup for openclaw-enterprise/runtime. If a tag exists, compare its digest with the source and use it only if it matches. Stop on a conflicting digest. The repository's immutability policy protects against overwrites but does not replace this readback or coordination with other writers.

Copy each absent tag and verify the destination digest before continuing. Run each command separately and stop on an error:

bash
skopeo copy --all --preserve-digests --authfile "$REGISTRY_AUTH" \  "docker://$SOURCE_CONTROLLER" "docker://$ECR_CONTROLLER:$ECR_TAG"skopeo copy --all --preserve-digests --authfile "$REGISTRY_AUTH" \  "docker://$SOURCE_RUNTIME" "docker://$ECR_RUNTIME:$ECR_TAG" skopeo inspect --raw --authfile "$REGISTRY_AUTH" \  "docker://$ECR_CONTROLLER:$ECR_TAG" | sha256sumskopeo inspect --raw --authfile "$REGISTRY_AUTH" \  "docker://$ECR_RUNTIME:$ECR_TAG" | sha256sum

Compare each printed hash, prefixed with sha256:, to the corresponding index digest in the receipt. Hashing the raw manifest checks the index itself, rather than selecting a platform-specific manifest. --all copies the platform manifests, and --preserve-digests fails if the registry cannot preserve them. If a copy times out or fails, read back that exact target tag before retrying: accept a matching digest, stop on a different digest, and retry only an absent tag after resolving the error. If readback itself fails, the outcome is unknown. Inspect both destinations, since one copy can succeed while the other fails.

Record the source and destination references with the chart and receipt. Exit the dedicated shell; its trap removes the auth file. In the original shell, export the verified readback digests:

bash
exit
bash
export CONTROLLER_IMAGE="$ECR_CONTROLLER@sha256:<verified-controller-digest>"export RUNTIME_IMAGE="$ECR_RUNTIME@sha256:<verified-runtime-digest>"

Configure node pull permissions separately. EKS managed nodes use their node IAM role for ECR pulls; cross-account repository policies may also be needed. For nodes without internet egress, configure ECR API and registry endpoints, S3 layer access, DNS, routes, security groups, and endpoint policies as described in ECR VPC endpoints and ECR images on EKS. Builder credentials do not grant node access. Confirm actual pulls on both control-plane and Agent nodes for the intended architectures. For direct GHCR pulls, provision and maintain approved private-registry credentials in every namespace or node that needs them; a workstation login is insufficient.

Continue at Configure the Installation with the two ECR digest references. Use the same runtime digest for gateway and Agent unless a separately verified pair is required.

Search documentation