Skip to content

Install in an air-gapped cluster

Install Omnia in a cluster that cannot reach ghcr.io, pulling every image from a registry you run.

  • A machine with internet access, a checkout of the Omnia repository at the release tag you want, helm (v3) and skopeo.
  • A container registry the cluster can pull from, and skopeo on a machine that can push to it.
  • The Helm install prerequisites for the cluster itself.
  1. On the connected machine, build the bundle for the version you are installing:

    Terminal window
    make airgap-bundle VERSION=0.9.0

    This writes dist/omnia-airgap-0.9.0/ and dist/omnia-airgap-0.9.0.tar. The bundle holds:

    PathContents
    oci/Every image, all platforms, in an OCI image layout
    images.txtThe source reference of each image
    charts/omnia-<version>.tgz with its subcharts, and omnia-crds-<version>.tgz
    schemas/The PromptPack JSON Schema
    manifest.jsonThe version and every image, chart and schema in the bundle
    mirror.shThe script for step 3
    SHA256SUMSChecksums of everything outside oci/

    The image list comes from rendering the packaged chart. It covers every image the chart deploys and every image the operator creates at runtime: agent facade and runtime, session-api, memory-api, privacy-api, eval workers, the policy broker, Arena workers, sandbox classes and the sandbox egress proxy. Every Omnia component is switched on for that render, so the bundle serves any combination you enable later.

  2. If your values enable an observability subchart (Prometheus, Grafana, Loki, Alloy, Tempo, KEDA or the NFS CSI driver), pass your values file so its images are bundled too:

    Terminal window
    make airgap-bundle VERSION=0.9.0 VALUES=values.yaml

    An image that no chart names, such as a custom spec.framework.image on an AgentRuntime, goes in a file of one reference per line, passed to hack/airgap-bundle.sh with --extra:

    Terminal window
    hack/airgap-bundle.sh 0.9.0 --out dist -f values.yaml --extra my-images.txt
  3. Carry the tarball across, unpack it, and push the images to your registry:

    Terminal window
    tar -xf omnia-airgap-0.9.0.tar
    cd omnia-airgap-0.9.0
    ./mirror.sh registry.internal.example.com .

    Each image keeps its path and loses its source registry host: ghcr.io/altairalabs/omnia-operator:0.9.0 lands at registry.internal.example.com/altairalabs/omnia-operator:0.9.0, and busybox:1.36 at registry.internal.example.com/busybox:1.36. A registry value with a path, such as registry.internal.example.com/omnia, prefixes it.

    Arguments after -- go to every skopeo copy. Use them for registry credentials or a private CA:

    Terminal window
    ./mirror.sh registry.internal.example.com . -- --dest-authfile auth.json

    Add --charts to also push both charts to oci://<registry>/charts. Add --dry-run to print each source and destination without copying.

  4. If the registry needs credentials, create a pull secret in the release namespace and in every workspace namespace. Pods the operator creates run in the workspace namespaces, and a pod can only use a Secret in its own namespace:

    Terminal window
    kubectl create secret docker-registry mirror-pull \
    --namespace omnia-system \
    --docker-server=registry.internal.example.com \
    --docker-username=<user> --docker-password=<token>
  5. Install the CRDs chart from the bundle:

    Terminal window
    helm install omnia-crds charts/omnia-crds-0.9.0.tgz \
    --namespace omnia-system --create-namespace
  6. Install the app chart from the bundle with global.imageRegistry, and global.imagePullSecrets if you made a secret:

    Terminal window
    helm install omnia charts/omnia-0.9.0.tgz \
    --namespace omnia-system \
    --set dashboard.auth.mode=builtin \
    --set global.imageRegistry=registry.internal.example.com \
    --set 'global.imagePullSecrets[0].name=mirror-pull' \
    -f values.yaml

    The chart rewrites every image it renders. It also passes the operator and the Arena controller the rewritten image of every pod they create, and passes global.imagePullSecrets on as their --image-pull-secrets flag. An image you set on a custom resource, such as spec.framework.image, spec.facades[].image or a SandboxClass you write, is used as written.

  7. Turn off every outbound call, since the cluster has no route to the endpoints: add --set airGapped=true to the install. It switches off install telemetry, Enterprise licence activation and its heartbeat, and the community Arena template sync from GitHub, keeps PromptPack schemas on the embedded copy, and fails the render if another value names an outside host. Outbound connections lists every destination and the value that controls it.

    Licence validation needs no network.

List every image the release namespace runs:

Terminal window
kubectl get pods -n omnia-system \
-o jsonpath='{range .items[*]}{range .spec.containers[*]}{.image}{"\n"}{end}{end}' | sort -u

Every line starts with your registry. Once you create an AgentRuntime, run the same command against its workspace namespace: the facade and runtime images start with your registry too.

A pod is in ImagePullBackOff with an image under your registry

Section titled “A pod is in ImagePullBackOff with an image under your registry”

The image is not in the mirror. Compare the image in kubectl describe pod with images.txt. An image missing from images.txt was not in the render the bundle was built from: rebuild with the values file that enables it, or add it with --extra.

A pod in a workspace namespace fails with unauthorized

Section titled “A pod in a workspace namespace fails with unauthorized”

The pull secret named in global.imagePullSecrets does not exist in that namespace. Create it there (step 4). The operator adds the secret’s name to the pod, and the kubelet reads it from the pod’s own namespace.

An observability subchart still pulls from its public registry

Section titled “An observability subchart still pulls from its public registry”

Each subchart reads its own registry setting, and only some read global.imageRegistry. The Helm Values Reference lists the key each one reads.