Project
Airgapped Registry
Private container registry for isolated environments - mirror upstream images, enforce provenance, and feed CI/CD without internet access.
Overview
Air-gapped environments can't pull images from Docker Hub or quay.io at runtime. This project mirrors a curated set of upstream images into an internal registry on a scheduled basis, signs them, and exposes them over mTLS so build pipelines and runtime nodes never need external network access.
Architecture
Registry
- Harbor - OCI-compliant registry with RBAC
- Docker Distribution - lightweight fallback runtime
- Notary / Cosign - image signing and verification
- TLS - internal CA, cert rotation via scripts
Sync Pipeline
- Skopeo - copy images between registries without a daemon
- Manifest list - sync multi-arch images (amd64 + arm64)
- Cron job - nightly pull from DMZ relay host
- Delta sync - layer dedup, only new layers transferred
CI/CD Integration
- GitLab CI -
DOCKER_HOSTpointed at internal registry - Build cache - push/pull cache layers across pipeline runs
- Promotion gates - dev → staging → prod image tagging workflow
- Vulnerability scan - Trivy on every push
Security
- No inbound internet - registry sits on isolated VLAN
- mTLS client certs - nodes must present cert to pull
- Image policy - unsigned or unscanned images blocked at admission
- Audit log - every pull, push, and delete recorded
Key Features
Capabilities
- Mirror list - declarative YAML defines what gets synced and from where
- Tag pinning - never pull
:latest, always resolve to a digest - Rollback - previous tag retained for 30 days before GC
- Helm chart mirror - OCI Helm charts stored and served alongside images
- Offline bootstrap - initial seed via portable drive, subsequent deltas over DMZ relay
- Health endpoint - monitoring integration confirms mirror freshness
Mirror Sync Quick Start
Copy an image into the internal registry:
skopeo copy \
docker://docker.io/library/nginx:1.27 \
docker://registry.internal/library/nginx:1.27 \
--dest-tls-verify=true --dest-cert-dir=/etc/registry/certs
Verify the digest matches upstream:
skopeo inspect docker://registry.internal/library/nginx:1.27 \
| jq '.Digest'