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_cronextension, provisioned as described in Provision the Database. If you are running PostgreSQL in-cluster with CloudNativePG, the custompg_cronimage 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)
Add the chart repository and find the current chart version:
helm repo add anchore https://charts.anchore.io helm search repo anchore/enterpriseNote the
CHART VERSIONcolumn from the output — this is different from the Anchore Enterprise application version (v6.1.0) and is whathelm pullexpects below.Download (pull) the chart archive:
export CHART_VERSION="<chart-version-from-above>" helm pull anchore/enterprise --version ${CHART_VERSION}This produces
enterprise-${CHART_VERSION}.tgzin the current directory.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.6The chart’skubectlImage(bitnamilegacy/kubectl:1.30) is only used by theosaaMigrationJobandupgradeJobduring major-version upgrades, not by a fresh install. If you plan to perform a major-version upgrade later, pull and mirror this image at that time.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-pgb. Pull the chart archive (the
APP VERSIONcolumn 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.yamlnow too, using the full example in Create the PostgreSQL cluster on the main Helm page as your starting point. LeaveimageNameas 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.yamlwith 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.
Option 1: Private Container Registry (Recommended)
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 tooThepostgresql-pgcronimage needs no separate re-tag step — build it directly against the same<registry>value you’re using here (docker build -t <registry>/postgresql-pgcron:17 .), as shown in step 4d above.Save the tagged images to a tarball, and transfer it — along with
enterprise-${CHART_VERSION}.tgz,cloudnative-pg-${CNPG_CHART_VERSION}.tgzandcnpg-cluster.yaml(if using CNPG), and yourlicense.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.tarPush 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>/chartsTraditional Helm chart repository (for example ChartMuseum, or a generic Artifactory Helm repo): upload
enterprise-${CHART_VERSION}.tgzper 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>
cnpg-system namespace and pass –set image.pullSecrets[0].name=<secret-name> above.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
kubectlImage to your registry at that time. It is not used during a fresh install.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.