Air-Gapped OpenShift with Quay Mirror
Deploy OpenShift in air-gapped environments with local Quay registry mirror, ImageDigestMirrorSet, and custom CatalogSources.
π‘ Quick Answer: Mirror all required images (GPU Operator, Network Operator, base images) to local Quay using
oc-mirror. Configure ImageDigestMirrorSet for automatic redirection, custom CatalogSources for OLM operators, and Quay CA trust for HTTPS.
The Problem
Air-gapped GPU clusters canβt pull images from public registries (nvcr.io, registry.k8s.io, quay.io). GPU Operator, Network Operator, driver containers, and CUDA images all need to be available locally. Without proper mirroring, operator installations fail and GPU workloads canβt start.
The Solution
Mirror Images with oc-mirror
# ImageSetConfiguration for GPU workloads
cat > imageset-config.yaml << 'EOF'
apiVersion: mirror.openshift.io/v1alpha2
kind: ImageSetConfiguration
mirror:
operators:
- catalog: registry.redhat.io/redhat/redhat-operator-index:v4.16
packages:
- name: gpu-operator-certified
- name: sriov-network-operator
- catalog: registry.redhat.io/redhat/certified-operator-index:v4.16
packages:
- name: nvidia-network-operator
additionalImages:
- name: nvcr.io/nvidia/driver:560.35.03-rhel9.4
- name: nvcr.io/nvidia/cuda:12.6.0-base-ubi9
- name: nvcr.io/nvidia/k8s-device-plugin:v0.16.2
- name: nvcr.io/nvidia/gpu-operator:v24.9.0
- name: nvcr.io/nvidia/dcgm-exporter:3.3.8-3.6.0
- name: nvcr.io/nvidia/k8s/container-toolkit:v1.16.2
EOF
# Run mirror to local Quay
oc mirror --config=imageset-config.yaml \
docker://quay.internal.example.com/mirror \
--dest-skip-tlsImageDigestMirrorSet
apiVersion: config.openshift.io/v1
kind: ImageDigestMirrorSet
metadata:
name: nvidia-mirrors
spec:
imageDigestMirrors:
- source: nvcr.io/nvidia
mirrors:
- quay.internal.example.com/mirror/nvidia
- source: nvcr.io/nim
mirrors:
- quay.internal.example.com/mirror/nim
- source: registry.k8s.io
mirrors:
- quay.internal.example.com/mirror/k8s
- source: docker.io
mirrors:
- quay.internal.example.com/mirror/dockerLocal CatalogSources
apiVersion: operators.coreos.com/v1alpha1
kind: CatalogSource
metadata:
name: nvidia-gpu-catalog
namespace: openshift-marketplace
spec:
sourceType: grpc
image: quay.internal.example.com/mirror/redhat/redhat-operator-index:v4.16
displayName: "NVIDIA GPU Operator (Mirrored)"
publisher: NVIDIA
updateStrategy:
registryPoll:
interval: 30m
---
apiVersion: operators.coreos.com/v1alpha1
kind: CatalogSource
metadata:
name: certified-operators-mirror
namespace: openshift-marketplace
spec:
sourceType: grpc
image: quay.internal.example.com/mirror/redhat/certified-operator-index:v4.16
displayName: "Certified Operators (Mirrored)"
publisher: Red HatQuay CA Trust
apiVersion: v1
kind: ConfigMap
metadata:
name: quay-ca
namespace: openshift-config
data:
ca-bundle.crt: |
-----BEGIN CERTIFICATE-----
... Quay internal CA certificate ...
-----END CERTIFICATE-----
---
apiVersion: config.openshift.io/v1
kind: Image
metadata:
name: cluster
spec:
additionalTrustedCA:
name: quay-caGlobal Pull Secret
# Add Quay credentials to global pull secret
oc set data secret/pull-secret -n openshift-config \
--from-file=.dockerconfigjson=<(
oc get secret pull-secret -n openshift-config \
-o jsonpath='{.data.\.dockerconfigjson}' | base64 -d | \
jq '.auths["quay.internal.example.com"] = {
"auth": "'$(echo -n "robot+mirror:TOKEN" | base64)'",
"email": "mirror@example.com"
}'
)graph TD
A[Public Registries] -->|oc-mirror| B[Bastion Host]
B -->|Push| C[Local Quay Registry]
C --> D[ImageDigestMirrorSet]
D --> E[OpenShift Nodes]
C --> F[CatalogSource]
F --> G[OLM Operator Install]
H[CA Trust ConfigMap] --> E
I[Global Pull Secret] --> E
G --> J[GPU Operator from mirror]
G --> K[Network Operator from mirror]Common Issues
- Operator install fails: ImagePullBackOff β IDMS not yet applied or MCO still rolling out; wait for all nodes to update
- CatalogSource stuck β mirrored index image may be incomplete; verify with
oc get catalogsource -n openshift-marketplace - CA trust not propagated β MCO must roll out to all nodes; check
oc get mcpfor progress - oc-mirror fails on large images β GPU driver images are 2-3GB; ensure bastion has enough disk and bandwidth
Best Practices
- Mirror all images before cluster installation β donβt discover missing images in production
- Use
oc-mirrorfor reproducible mirror operations β saves ImageSetConfiguration as manifest - Test operator installation from mirror on a staging cluster first
- Keep mirror in sync with a scheduled
oc-mirrorcron job on bastion - Document the mirror contents in Git alongside ImageDigestMirrorSet configs
Key Takeaways
- Air-gapped GPU clusters need local mirrors for all NVIDIA container images
oc-mirrorhandles bulk mirroring with ImageSetConfiguration- ImageDigestMirrorSet redirects image pulls to local Quay transparently
- Custom CatalogSources point OLM to mirrored operator indexes
- CA trust and pull secrets must be configured cluster-wide before operator installation

Recommended
Kubernetes Recipes β The Complete Book100+ production-ready patterns with detailed explanations, best practices, and copy-paste YAML. Everything in one place.
Get the Book βLearn by Doing
CopyPasteLearn β Hands-on Cloud & DevOps CoursesMaster Kubernetes, Ansible, Terraform, and MLOps with interactive, copy-paste-run lessons. Start free.
Browse Courses βπ Deepen Your Skills β Hands-on Courses
Courses by CopyPasteLearn.com β Learn IT by Doing
