Skip to main content

Air-Gap Mirroring

Purpose: For operators, shows how to mirror images for disconnected environments.

Task Summary

Air-gap mirroring packages every container image required by the openCenter platform into a single Zarf artifact. This artifact is transferred to a disconnected site where a bastion host serves images from a local registry.

Prerequisites

  • A connected build host with internet access (Zone A)
  • opencenter-airgap CLI installed (see Air-Gap CLI Reference)
  • Docker or Podman running on the build host
  • Sufficient disk space — a full platform package is typically 15–25 GB compressed
  • Physical media or approved transfer mechanism for moving the artifact to the air-gapped site

Steps

1. Initialize the Air-Gap Configuration

opencenter-airgap init my-airgap-cluster

This creates a config/ directory with versions.env listing every image, Helm chart, and binary version.

2. Pin Versions

Edit config/versions.env to lock all component versions:

# config/versions.env
K8S_VERSION=1.29.4
FLUX_VERSION=2.3.0
CERT_MANAGER_VERSION=1.18.2
KYVERNO_VERSION=1.12.0
HARBOR_VERSION=2.11.0
LONGHORN_VERSION=1.7.0

3. Build the Zarf Package

opencenter-airgap build

The build process:

  1. Pulls every image listed in the platform manifest from upstream registries (Docker Hub, Quay.io, ghcr.io).
  2. Pulls Helm charts and Kubernetes binaries.
  3. Packages OS dependencies and Python packages required by Kubespray.
  4. Bundles everything into a signed Zarf artifact: zarf-package-my-airgap-cluster-amd64.tar.zst.
  5. Generates an SBOM for the entire package.

Build time depends on network speed — expect 30–60 minutes for a full platform package.

4. Transfer to the Air-Gapped Site

Move the .tar.zst file to the disconnected environment using your approved transfer mechanism (physical media, data diode, cross-domain solution).

5. Deploy on the Bastion Host

On the bastion host inside the air-gapped network:

zarf package deploy zarf-package-my-airgap-cluster-amd64.tar.zst --confirm

After deployment, the bastion serves:

ServicePortContent
Container registry5000All platform and application images
Package repository8080OS packages, Python dependencies
Helm chart repository8081All Helm charts from openCenter-gitops-base

6. Configure Cluster Nodes to Use the Bastion Registry

Point containerd on each node to the bastion registry. In the Kubespray inventory:

# group_vars/all/containerd.yml
containerd_registries_mirrors:
- prefix: "docker.io"
mirrors:
- host: "http://bastion.local:5000"
capabilities: ["pull", "resolve"]
- prefix: "ghcr.io"
mirrors:
- host: "http://bastion.local:5000"
capabilities: ["pull", "resolve"]
- prefix: "quay.io"
mirrors:
- host: "http://bastion.local:5000"
capabilities: ["pull", "resolve"]

Verification

Confirm the bastion registry contains the expected images:

# List repositories in the bastion registry
curl -s http://bastion.local:5000/v2/_catalog | jq .

# Verify a specific image is available
skopeo inspect docker://bastion.local:5000/platform-security/cert-manager-controller:1.18.2

From a cluster node, verify image pull works:

crictl pull bastion.local:5000/platform-core/fluxcd/source-controller:2.3.0

Troubleshooting

SymptomCauseFix
zarf package deploy fails with checksum errorCorrupted transferRe-transfer the artifact; verify SHA256 before deploy
Node can't pull from bastioncontainerd mirror not configuredCheck containerd_registries_mirrors in Kubespray inventory
Missing image in bastion registryImage not in versions.envAdd the image, rebuild, and redeploy the Zarf package

Further Reading