This is the multi-page printable view of this section. Click here to print.

Return to the regular view of this page.

Deploy Air-Gapped using Helm

Anchore Enterprise can run in an air-gapped Kubernetes cluster with no outbound internet access. The air-gapped-specific work is getting the Helm chart and container images into a registry reachable from the cluster: you pull and package them on an internet-connected system, then move the tarball to the destination network(s). Once the chart and images are in place, deployment follows the standard Kubernetes with Helm procedure.

Configure Air-Gapped Feed Handling

Anchore Enterprise syncs vulnerability feed data from the Anchore Data Service automatically. In an air-gapped cluster this sync cannot reach the internet, so before deploying you must disable the Data Syncer’s automatic feed sync in your values file, and plan to download and import feed bundles manually with AnchoreCTL once the deployment is running. Both steps are described in Air-Gapped Feed Configuration.

Prerequisites

  • Low side (internet-facing) — Helm v3.8 or above and the Docker static binary, used only to pull and package the chart and images.
  • Low side (internet-facing) — Docker or Podman, if you are running PostgreSQL in-cluster with CloudNativePG and need to build the pg_cron-enabled image.
  • High side (air-gapped) — A Kubernetes cluster, kubectl, and Helm v3.8 or above, used to run Anchore Enterprise. See Prerequisites on the main Helm page for supported Kubernetes versions.
  • A private container registry reachable from every node in the cluster. Unlike Docker Compose, Kubernetes has no cluster-wide equivalent of docker load, so a registry is required for any deployment beyond a single-node test cluster. See Option 2 below for a node-local fallback on small clusters.
  • An external PostgreSQL 17 database with the pg_cron extension, provisioned as described in Provision the Database. If you are running PostgreSQL in-cluster with CloudNativePG, the custom pg_cron image you build in that section is pushed to a registry of your choosing, so that step is already air-gap-friendly.

Detailed sizing and other requirements are in System Requirements.

Prepare the Chart and Images (low side)

  1. Add the chart repository and find the current chart version:

    helm repo add anchore https://charts.anchore.io
    helm search repo anchore/enterprise
    

    Note the CHART VERSION column from the output — this is different from the Anchore Enterprise application version (v6.1.0) and is what helm pull expects below.

  2. Download (pull) the chart archive:

    export CHART_VERSION="<chart-version-from-above>"
    
    helm pull anchore/enterprise --version ${CHART_VERSION}
    

    This produces enterprise-${CHART_VERSION}.tgz in the current directory.

  3. Pull the images the chart deploys:

    docker pull docker.io/anchore/enterprise:v6.1.0
    docker pull docker.io/anchore/enterprise-ui:v6.1.0
    docker pull docker.io/redis:7.4.6
    
  4. If you are running PostgreSQL in-cluster with CloudNativePG, the operator itself also needs mirroring — its chart and operator image are not part of the Anchore chart:

    a. Add the CNPG chart repository and find its current version:

    helm repo add cnpg https://cloudnative-pg.github.io/charts
    helm repo update
    helm search repo cnpg/cloudnative-pg
    

    b. Pull the chart archive (the APP VERSION column from the search above is the operator’s default image tag, needed next):

    export CNPG_CHART_VERSION="<cnpg-chart-version-from-above>"
    
    helm pull cnpg/cloudnative-pg --version ${CNPG_CHART_VERSION}
    

    This produces cloudnative-pg-${CNPG_CHART_VERSION}.tgz.

    c. Pull the operator image:

    docker pull ghcr.io/cloudnative-pg/cloudnative-pg:<operator-version-from-above>
    

    d. Build your pg_cron-enabled PostgreSQL image, as described in Run PostgreSQL In-Cluster with CloudNativePG on the main Helm page — tag it directly with the <registry> value you’ll use in Move the Chart and Images to the High Side below (docker build -t <registry>/postgresql-pgcron:17 .), so it needs no separate re-tag step.

    e. Save cnpg-cluster.yaml now too, using the full example in Create the PostgreSQL cluster on the main Helm page as your starting point. Leave imageName as a placeholder for now — you’ll fill in the actual <registry> reference on the high side in Install CloudNativePG and Provision the Database below, once the image has actually been pushed there.

    Include the CNPG chart archive, both images, and cnpg-cluster.yaml with everything else when you save and transfer them below — there is no direct network path from the low side to the high side’s registry, so they all have to travel together.

Move the Chart and Images to the High Side

Choose one of the following. A private container registry is the recommended path for the images; use node-local import only if no registry is reachable from the cluster.

  1. Re-tag the images for your private registry, replacing <registry> with your registry domain (for example, core.harbor.domain):

    docker tag docker.io/anchore/enterprise:v6.1.0 \
      <registry>/anchore/enterprise:v6.1.0
    docker tag docker.io/anchore/enterprise-ui:v6.1.0 \
      <registry>/anchore/enterprise-ui:v6.1.0
    docker tag docker.io/redis:7.4.6 <registry>/redis:7.4.6
    # If using CNPG in-cluster (step 4 above), also re-tag the operator image:
    docker tag ghcr.io/cloudnative-pg/cloudnative-pg:<operator-version> \
      <registry>/cloudnative-pg/cloudnative-pg:<operator-version>
    # If mirroring bitnamilegacy/kubectl:1.30 for a future upgrade, re-tag that too
    
  2. Save the tagged images to a tarball, and transfer it — along with enterprise-${CHART_VERSION}.tgz, cloudnative-pg-${CNPG_CHART_VERSION}.tgz and cnpg-cluster.yaml (if using CNPG), and your license.yaml — to the high side. There is no direct network path between the low side and the high side’s registry, so this tarball, moved across the air gap by whatever transfer process your organization uses, is how everything gets there:

    # Low side
    docker save -o anchore-airgap-images.tar \
      <registry>/anchore/enterprise:v6.1.0 \
      <registry>/anchore/enterprise-ui:v6.1.0 \
      <registry>/redis:7.4.6
      # <registry>/cloudnative-pg/cloudnative-pg:<operator-version> \  # if using CNPG in-cluster
      # <registry>/postgresql-pgcron:17 \                             # if using CNPG in-cluster
      # <registry>/bitnamilegacy/kubectl:1.30                         # if mirroring for a future upgrade
    
    # High side, after transferring anchore-airgap-images.tar, enterprise-${CHART_VERSION}.tgz,
    # cloudnative-pg-${CNPG_CHART_VERSION}.tgz, and license.yaml across
    docker load -i anchore-airgap-images.tar
    
  3. Push the loaded images to your private registry from the high side:

    docker push <registry>/anchore/enterprise:v6.1.0
    docker push <registry>/anchore/enterprise-ui:v6.1.0
    docker push <registry>/redis:7.4.6
    # docker push <registry>/cloudnative-pg/cloudnative-pg:<operator-version>
    # docker push <registry>/postgresql-pgcron:17
    # docker push <registry>/bitnamilegacy/kubectl:1.30
    

Option 2: Local Import onto Cluster Nodes

For small or single-node clusters with no registry available, images can be imported directly into each node’s container runtime instead — for example with ctr images import (containerd), crictl on nodes where it supports import, or a distribution-specific helper such as k3s ctr images import. Save the images as in step 2 above, transfer the tarball to every node, and import it there.

Push the Chart to an Internal Helm Repository or GitOps Source

If you use GitOps tooling (ArgoCD, Flux) or otherwise deploy from your own internal Helm repository on the high side, transferring the .tgz to a bastion host is not enough on its own — GitOps controllers pull charts from a repository URL declared in a manifest, not from a local file. Push the chart there from the low side, or from any system that can reach it:

  • OCI registry (Harbor, Artifactory, Amazon ECR, Azure ACR, Google Artifact Registry, and most modern registries support OCI Helm charts):

    helm push enterprise-${CHART_VERSION}.tgz oci://<registry>/charts
    
  • Traditional Helm chart repository (for example ChartMuseum, or a generic Artifactory Helm repo): upload enterprise-${CHART_VERSION}.tgz per that repository’s own process, then regenerate its index if required (helm repo index).

Reference oci://<registry>/charts/enterprise (or your chart repository URL) and chartVersion: ${CHART_VERSION} in your ArgoCD Application or Flux HelmRelease, alongside the image overrides described below.

Deploy on the High Side

Besides the images, the air-gapped cluster needs enterprise-${CHART_VERSION}.tgz and your license.yaml available to kubectl/helm — or, if you pushed the chart to an internal repository above, its OCI/chart-repo reference available to your GitOps controller. Create the namespace and license secret exactly as described in Install the Chart on the main Helm page.

Create the image pull secret for your private registry instead of Docker Hub:

export NAMESPACE=anchore
export REGISTRY="<registry>"
export REGISTRY_USER="username"
export REGISTRY_PASSWORD="password"
export REGISTRY_EMAIL="[email protected]"

kubectl create secret docker-registry anchore-enterprise-pullcreds --docker-server=${REGISTRY} --docker-username=${REGISTRY_USER} --docker-password=${REGISTRY_PASSWORD} --docker-email=${REGISTRY_EMAIL} -n ${NAMESPACE}

Install CloudNativePG and Provision the Database

If you are running PostgreSQL in-cluster with CloudNativePG, install the operator and create the database now, before installing Anchore Enterprise — the values file below needs the resulting database endpoint.

Install the operator from the local chart archive, pointing its image at your private registry instead of ghcr.io:

helm install cnpg ./cloudnative-pg-${CNPG_CHART_VERSION}.tgz \
  --namespace cnpg-system --create-namespace \
  --set image.repository=<registry>/cloudnative-pg/cloudnative-pg \
  --set image.tag=<operator-version>

Then update the imageName field in the cnpg-cluster.yaml you brought over from the low side (see step 4e in Prepare the Chart and Images), now that the image has actually been pushed to your registry:

spec:
  imageName: <registry>/postgresql-pgcron:17

Apply it and wait for the cluster to report ready — the anchore-pg-rw service it creates is what you’ll use as postgresql.externalEndpoint in the Anchore values file below:

kubectl apply -f cnpg-cluster.yaml
kubectl get cluster anchore-pg -n ${NAMESPACE} -w

Point every image at your air-gapped registry in anchore_values.yaml, in addition to the database and license/pull-secret settings described in Install the Chart on the main Helm page:

licenseSecretName: anchore-enterprise-license
imagePullSecretName: anchore-enterprise-pullcreds

image: <registry>/anchore/enterprise:v6.1.0

ui:
  image: <registry>/anchore/enterprise-ui:v6.1.0

ui-redis:
  image:
    registry: <registry>
    repository: redis
    tag: 7.4.6
    pullSecrets:
      - anchore-enterprise-pullcreds

postgresql:
  externalEndpoint: <DB_HOSTNAME>   # e.g. anchore-pg-rw.anchore.svc if using CNPG above, or your managed DB endpoint
  auth:
    username: <DB_USERNAME>
    password: <DB_PASSWORD>
    database: <DB_NAME>
  port: 5432

Install from the local chart archive rather than the chart repository, since the high side cannot reach charts.anchore.io:

export NAMESPACE=anchore
export RELEASE=anchore
export CHART_VERSION="<chart-version>"

helm install ${RELEASE} -n ${NAMESPACE} ./enterprise-${CHART_VERSION}.tgz -f anchore_values.yaml

If you pushed the chart to an internal OCI registry or Helm repository instead, install from that reference — or configure your GitOps controller to do so — rather than the local file:

helm install ${RELEASE} -n ${NAMESPACE} oci://<registry>/charts/enterprise --version ${CHART_VERSION} -f anchore_values.yaml

Check pod status as described in Install the Chart — every pod should reach READY 1/1 and STATUS Running.

Once the deployment is running, continue with Post-Installation Steps on the main Helm page to verify it with anchorectl, including uploading your first feed bundle as described in Configure Air-Gapped Feed Handling above.