Veröffentlicht am: 12. März 2026
7 Minuten Lesezeit
Mehrere Registries hinter einem Endpunkt – GitLab Container Virtual Registry mit Docker Hardened Images, Caching und Audit-Trail.

Wer im Plattformteam arbeitet, kennt solche Gespräche:
„Security sagt: Wir müssen gehärtete Base-Images verwenden."
„Prima – wo trage ich jetzt die Credentials für noch eine weitere Registry ein?"
„Und wie stellen wir sicher, dass alle sie auch wirklich nutzen?"
Oder diese hier:
„Warum sind unsere Builds so langsam?"
„Wir pullen dasselbe 500-MB-Image in jedem einzelnen Job neu von Docker Hub."
„Kann man die nicht irgendwo cachen?"
Ich arbeite bei GitLab an der Container Virtual Registry – einem Pull-Through-Cache, der vor den vorgelagerten Registries sitzt: Docker Hub, dhi.io (Docker Hardened Images), MCR und Quay. Teams erhalten einen einzigen Endpunkt zum Pullen. Images werden beim ersten Abruf gecacht; alle nachfolgenden Pulls kommen aus dem Cache. Das Entwicklungsteam muss nicht wissen, aus welchem Upstream ein bestimmtes Image stammt.
Dieser Artikel zeigt die Einrichtung der Container Virtual Registry – mit Docker Hardened Images als konkretem Anwendungsfall, da diese Kombination für Teams mit Sicherheitsanforderungen besonders naheliegt.
Die Plattformteams, mit denen ich spreche, verwalten Container-Images über drei bis fünf Registries:
Jede davon hat eigene Authentifizierungsmechanismen, unterschiedliche Netzwerklatenz und eine eigene Pfadstruktur für Images.
CI/CD-Konfigurationen füllen sich mit registry-spezifischer Logik. Credential-Management wird zum eigenständigen Projekt. Und jeder Pipeline-Job lädt dieselben Base-Images erneut über das Netz – obwohl sie sich seit Wochen nicht geändert haben.
Container Virtual Registry konsolidiert das: eine Registry-URL, ein Authentifizierungsfluss über GitLab, gecachte Images aus GitLab-Infrastruktur statt wiederholter Internet-Traversierung.
Das Modell ist geradlinig:
Pipeline ruft ab:
gitlab.com/virtual_registries/container/1000016/python:3.13
Virtual Registry prüft:
1. Im Cache vorhanden? → Direkt zurückgeben
2. Nein? → Vom Upstream laden, cachen, zurückgeben
Upstreams werden in Prioritätsreihenfolge konfiguriert. Bei einem eingehenden Pull-Request durchsucht die Virtual Registry die Upstreams der Reihe nach, bis das Image gefunden wird. Das Ergebnis wird für einen konfigurierbaren Zeitraum gecacht – standardmäßig 24 Stunden.
┌─────────────────────────────────────────────────────────┐ │ CI/CD Pipeline │ │ │ │ │ ▼ │ │ gitlab.com/virtual_registries/container/<id>/image │ └─────────────────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────┐ │ Container Virtual Registry │ │ │ │ Upstream 1: Docker Hub ────────────────┐ │ │ Upstream 2: dhi.io (Hardened) ────────┐│ │ │ Upstream 3: MCR ─────────────────────┐││ │ │ Upstream 4: Quay.io ────────────────┐│││ │ │ ││││ │ │ ┌─────────────────┴┴┴┴──┐ │ │ │ Cache │ │ │ │ (manifests + layers) │ │ │ └───────────────────────┘ │ └─────────────────────────────────────────────────────────┘
Docker Hardened Images zeichnen sich durch minimale Angriffsfläche, nahezu keine bekannten CVEs, vollständige Software Bills of Materials (SBOMs) und SLSA-Provenance aus. Für Teams, die Base-Images für sicherheitskritische Workloads evaluieren, gehören sie auf die Shortlist.
Der Wechsel zu dhi.io erzeugt jedoch dieselbe operative Reibung wie jede neue Registry:
Die Virtual Registry löst jeden dieser Punkte:
Einzelne Credential: Teams authentifizieren sich bei GitLab. Die Virtual Registry übernimmt die Upstream-Authentifizierung. Docker-Credentials werden einmalig auf Registry-Ebene konfiguriert und gelten für alle Pulls.
Keine per-Team-CI/CD-Änderungen: Pipelines auf die Virtual Registry zeigen lassen – fertig. Die Upstream-Konfiguration ist zentralisiert.
Schrittweise Einführung: Da Images mit ihrem vollständigen Pfad gecacht werden, ist im Cache sichtbar, was tatsächlich abgerufen wird. Wird library/python:3.11 statt der gehärteten Variante gepullt, ist das erkennbar.
Audit-Trail: Der Cache zeigt exakt, welche Images aktiv genutzt werden – nachvollziehbar für Compliance-Zwecke und als Grundlage für das Verständnis der tatsächlichen Infrastruktur-Abhängigkeiten.
Wer das Konzept verstanden hat und die Einrichtung zu einem späteren Zeitpunkt in Angriff nimmt: Die wesentlichen Konzepte sind damit abgedeckt. Die technische Konfiguration folgt im nächsten Abschnitt.
Die folgende Einrichtung nutzt den Python-Client aus dem Demo-Projekt.
from virtual_registry_client import VirtualRegistryClient
client = VirtualRegistryClient()
registry = client.create_virtual_registry(
group_id="785414", # ID der obersten Gruppe
name="platform-images",
description="Cached container images for platform teams"
)
print(f"Registry ID: {registry['id']}") # Diese ID wird für die Pull-URL benötigt
Für offizielle Images wie Alpine, Python usw.:
docker_upstream = client.create_upstream(
registry_id=registry['id'],
url="https://registry-1.docker.io",
name="Docker Hub",
cache_validity_hours=24
)
Docker Hardened Images werden auf dhi.io gehostet – einer separaten Registry mit Authentifizierungspflicht:
dhi_upstream = client.create_upstream(
registry_id=registry['id'],
url="https://dhi.io",
name="Docker Hardened Images",
username="your-docker-username",
password="your-docker-access-token",
cache_validity_hours=24
)
# MCR für .NET-Teams client.create_upstream(
registry_id=registry['id'],
url="https://mcr.microsoft.com",
name="Microsoft Container Registry",
cache_validity_hours=48
)
# Quay für das Red-Hat-Ökosystem client.create_upstream(
registry_id=registry['id'],
url="https://quay.io",
name="Quay.io",
cache_validity_hours=24
)
Eine .gitlab-ci.yml, die über die Virtual Registry pullt:
variables:
VIRTUAL_REGISTRY_ID: <your_virtual_registry_ID>
build:
image: docker:24
services:
- docker:24-dind
before_script:
# Authentifizierung bei GitLab – Upstream-Auth wird übernommen
- echo "${CI_JOB_TOKEN}" | docker login -u gitlab-ci-token --password-stdin gitlab.com
script:
# Alle Pulls laufen über die zentrale Virtual Registry
# Offizielle Docker Hub Images (library/-Präfix erforderlich)
- docker pull gitlab.com/virtual_registries/container/${VIRTUAL_REGISTRY_ID}/library/alpine:latest
# Docker Hardened Images von dhi.io (kein Präfix nötig)
- docker pull gitlab.com/virtual_registries/container/${VIRTUAL_REGISTRY_ID}/python:3.13
# .NET von MCR
- docker pull gitlab.com/virtual_registries/container/${VIRTUAL_REGISTRY_ID}/dotnet/sdk:8.0
Verschiedene Registries verwenden unterschiedliche Pfadkonventionen:
| Registry | Beispiel-Pull-URL |
|---|---|
| Docker Hub (offiziell) | .../library/python:3.11-slim |
| Docker Hardened Images (dhi.io) | .../python:3.13 |
| MCR | .../dotnet/sdk:8.0 |
| Quay.io | .../prometheus/prometheus:latest |
Nach einigen Pulls lässt sich der Cache überprüfen:
upstreams = client.list_registry_upstreams(registry['id']) for upstream in upstreams:
entries = client.list_cache_entries(upstream['id'])
print(f"{upstream['name']}: {len(entries)} cached entries")
Testergebnisse beim Pullen über die Virtual Registry:
| Messgröße | Ohne Cache | Mit warmem Cache |
|---|---|---|
| Pull-Zeit (Alpine) | 10,3 s | 4,2 s |
| Pull-Zeit (Python 3.13 DHI) | 11,6 s | ~4 s |
| Netzwerk-Roundtrips zum Upstream | Jeder Pull | Nur Cache-Misses |
Der erste Pull hat dieselbe Dauer – das Image muss vom Upstream geladen werden. Jeder weitere Pull innerhalb der Cache-Gültigkeitsdauer kommt direkt aus GitLab-Storage: kein Netzwerk-Hop zu Docker Hub, dhi.io, MCR oder einer anderen Registry.
Bei Teams mit vielen Pipeline-Jobs pro Tag summiert sich das zu einem messbaren Gewinn bei den Build-Laufzeiten.
Der Standard sind 24 Stunden. Für sicherheitskritische Images, bei denen Patches schnell verfügbar sein sollen, empfiehlt sich ein kürzeres Intervall:
client.create_upstream(
registry_id=registry['id'],
url="https://dhi.io",
name="Docker Hardened Images",
username="your-username",
password="your-token",
cache_validity_hours=12
)
Für stabile Images mit fixen Versions-Tags ist ein längeres Intervall problemlos.
Upstreams werden der Reihe nach geprüft. Bei gleichnamigen Images in verschiedenen Registries gewinnt der erste passende Upstream.
Virtual Registries und Upstreams lassen sich auch direkt in der GitLab-Oberfläche einrichten – ohne API-Aufrufe. Unter Einstellungen > Pakete und Registries > Virtual Registry der jeweiligen Gruppe stehen folgende Optionen zur Verfügung:
In Entwicklung:
Container Virtual Registry befindet sich in der Beta-Phase. Die Funktion wird produktiv eingesetzt und wird weiterentwickelt – Feedback fließt direkt in die Priorisierung ein.
Wer als Plattformteam mit Registry-Wildwuchs zu kämpfen hat: Ich möchte verstehen, wie die aktuelle Situation aussieht.
Erfahrungen und Rückmeldungen gerne im Container Virtual Registry Feedback-Issue teilen.
Teams, die sicherheitsgehärtete Base-Images mit vollständigen SBOMs und SLSA-Provenance einsetzen, haben möglicherweise auch Compliance-Überlegungen – beispielsweise in Bereichen wie Sicherheit der Software-Lieferkette, Nachvollziehbarkeit von Image-Abhängigkeiten und zentralem Audit-Trail.
Regulatorische Frameworks wie NIS2 und der Cyber Resilience Act adressieren ähnliche Themen rund um Software-Lieferketten und SBOM-Transparenz. Für konkrete Compliance-Anforderungen empfiehlt sich Rücksprache mit entsprechender Fachberatung.
Hat dir dieser Blogbeitrag gefallen? Hast du Fragen oder Feedback? Erstelle ein neues Diskussionsthema im GitLab-Community-Forum und lass andere an deinen Eindrücken teilhaben.
Feedback teilen