Install in an Air-Gapped Cluster
An offline installation needs internal copies of every chart, image, plugin, bundle, and identity endpoint used by Varroa or Jenkins.
Mirror Artifacts
Section titled “Mirror Artifacts”| Artifact | Include |
|---|---|
| Varroa | Chart, backend, and dashboard |
| Dependencies | Enabled NATS and Dex images |
| Jenkins | Images selected by version profiles |
| Agents | Images referenced by bundles |
| Plugins | Packs for permitted profiles |
| Configuration | Internal Git or OCI artifacts |
Render the exact values and inspect every image before transfer:
helm template varroa oci://ghcr.io/varroaci/charts/varroa \ --version <version> -n varroa-system -f values-airgap.yaml > rendered.yamlgrep 'image:' rendered.yamlExport Plugin Packs
Section titled “Export Plugin Packs”Run this in a connected environment for every profile that offline controllers may use:
varroactl export plugins \ --profile jenkins-version-2-570 \ --to tar:///transfer/plugins-2-570.tar.gzThe command resolves the plugin closure, downloads it, verifies checksums, and creates an OCI plugin pack. --to also accepts oci:// and dir://. Use --registry-config for registry credentials. Use --insecure only for plain HTTP on a trusted isolated network.
Configure Internal Images
Section titled “Configure Internal Images”Create the pull secret in the Varroa namespace and in every controller namespace:
kubectl create namespace varroa-systemkubectl create secret docker-registry registry-credentials \ -n varroa-system \ --docker-server=registry.example.com \ --docker-username='<user>' \ --docker-password='<password>'global: imagePullSecrets: - name: registry-credentialsoperator: image: {repository: registry.example.com/varroa-jenkins, tag: "<version>"}gateway: image: {repository: registry.example.com/varroa-jenkins, tag: "<version>"}bff: image: {repository: registry.example.com/varroa-jenkins, tag: "<version>"}frontend: image: {repository: registry.example.com/varroa-jenkins-frontend, tag: "<version>"}Mirror and override enabled dependency images too. Set ProvisioningDefaults.spec.imagePullSecrets for controller pods. Each named Secret must exist in the controller namespace.
Enable the Offline Update Center
Section titled “Enable the Offline Update Center”Use local storage when one update-center replica is sufficient:
updateCenter: enabled: true storage: type: local local: size: 20Gi pullThrough: enabled: false
networkPolicy: enabled: true pullThroughEgress: enabled: falseFor shared registry storage, use updateCenter.storage.type: oci with an internal ref and existingSecret. Allow only that registry through networkPolicy.updateCenterRegistryEgress.
Install from an internal chart registry:
helm install varroa oci://registry.example.com/charts/varroa \ --version <version> -n varroa-system --create-namespace \ -f values-airgap.yamlImport Plugin Packs
Section titled “Import Plugin Packs”export VARROACTL_UC_TOKEN=$(kubectl get secret \ varroa-updatecenter-import-token -n varroa-system \ -o jsonpath='{.data.token}' | base64 -d)kubectl port-forward -n varroa-system svc/varroa-updatecenter 8080:8080In another shell:
varroactl import \ --from tar:///transfer/plugins-2-570.tar.gz \ --to uc://localhost:8080--from accepts oci://, dir://, and tar://. uc:// is valid only for --to and requires VARROACTL_UC_TOKEN.
Require StorageReady=True, CoverageComplete=True, and Ready=True before provisioning offline controllers:
kubectl get updatecenter varroa-update-center -o yamlstatus.gaps lists missing plugin versions. With pull-through disabled, an affected controller reports WaitingForUpdateCenter and remains blocked until coverage is complete.
Enforce the Offline Boundary
Section titled “Enforce the Offline Boundary”The chart isolates only varroa-system. Apply default-deny egress and explicit allow rules in every controller namespace. Allow DNS, the Kubernetes API when needed, the mite gateway, internal registries, internal Git or OCI sources, the update center, identity services, and approved build destinations.
Test from the control-plane, Jenkins, and agent pods. Public registry and Jenkins update-service connections must fail while all internal endpoints remain reachable.
Verify
Section titled “Verify”Create a controller using an imported version profile, then check:
kubectl get controller -Akubectl get updatecenter varroa-update-center \ -o jsonpath='{.status.phase}{"\n"}{.status.gaps}{"\n"}'The controller should reach Connected. Run a build that creates an agent pod to verify its image was mirrored.
| Symptom | Check |
|---|---|
ImagePullBackOff | Repository, tag, architecture, and namespace pull secret. |
| Update center not ready | PVC binding or internal registry reachability. |
| Coverage incomplete | status.gaps and imported profile packs. |
| Import returns 401 | Import token and update-center Secret. |
See Update Center and Plugin packs.