Veröffentlicht am: 12. März 2026

7 Minuten Lesezeit

GitLab Container Virtual Registry mit Docker Hardened Images einrichten

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.

Das Problem: Registry-Wildwuchs im Plattformteam

Die Plattformteams, mit denen ich spreche, verwalten Container-Images über drei bis fünf Registries:

  • Docker Hub für die meisten Base-Images
  • dhi.io für Docker Hardened Images (sicherheitskritische Workloads)
  • MCR für .NET- und Azure-Tooling
  • Quay.io für das Red-Hat-Ökosystem
  • Interne Registries für proprietäre Images

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.

Funktionsweise

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) │            │ │                    └───────────────────────┘            │ └─────────────────────────────────────────────────────────┘


    

Was das konkret bringt – besonders mit Docker Hardened Images

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:

  • Credential-Verteilung: Docker-Credentials müssen auf alle Systeme verteilt werden, die Images von dhi.io abrufen.
  • CI/CD-Anpassungen: Jede Pipeline muss für die Authentifizierung mit dhi.io aktualisiert werden.
  • Akzeptanzproblem: Ohne zentrale Steuerung greifen Teams weiterhin auf reguläre Images zurück.
  • Fehlende Transparenz: Ob Teams tatsächlich die gehärteten Varianten nutzen, ist kaum nachvollziehbar.

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.

Einrichtung

Die folgende Einrichtung nutzt den Python-Client aus dem Demo-Projekt.

Virtual Registry erstellen

      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

    

Docker Hub als Upstream hinzufügen

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 (dhi.io) hinzufügen

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
)

    

Weitere Upstreams hinzufügen

      
# 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
)

    

CI/CD aktualisieren

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

    

Image-Pfadformate

Verschiedene Registries verwenden unterschiedliche Pfadkonventionen:

RegistryBeispiel-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

Funktionsprüfung

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")

    

Messergebnisse

Testergebnisse beim Pullen über die Virtual Registry:

MessgrößeOhne CacheMit warmem Cache
Pull-Zeit (Alpine)10,3 s4,2 s
Pull-Zeit (Python 3.13 DHI)11,6 s~4 s
Netzwerk-Roundtrips zum UpstreamJeder PullNur 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.

Praktische Hinweise

Cache-Gültigkeit

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.

Upstream-Priorität

Upstreams werden der Reihe nach geprüft. Bei gleichnamigen Images in verschiedenen Registries gewinnt der erste passende Upstream.

Limits

  • Maximal 20 Virtual Registries pro Gruppe
  • Maximal 20 Upstreams pro Virtual Registry

Konfiguration über die Oberfläche

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:

  • Virtual Registries erstellen und verwalten
  • Upstreams hinzufügen, bearbeiten und neu anordnen
  • Cache anzeigen und verwalten
  • Überblick, welche Images abgerufen werden

Ausblick

In Entwicklung:

  • Allow/Deny-Listen: Regex-basierte Steuerung, welche Images aus welchen Upstreams abgerufen werden dürfen.

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.

Feedback

Wer als Plattformteam mit Registry-Wildwuchs zu kämpfen hat: Ich möchte verstehen, wie die aktuelle Situation aussieht.

  • Wie viele Upstream-Registries werden verwaltet?
  • Wo liegt der größte Schmerzpunkt?
  • Würde ein solcher Ansatz helfen – und falls nicht: Was fehlt?

Erfahrungen und Rückmeldungen gerne im Container Virtual Registry Feedback-Issue teilen.

Weiterführende Ressourcen

Für deutsche Unternehmen könnte dies folgende Themen betreffen

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.

Feedback erwünscht

Dieser Blogbeitrag hat gefallen oder es gibt Fragen oder Feedback? Ein neues Diskussionsthema im GitLab-Community-Forum erstellen und Eindrücke austauschen.

Feedback teilen

Beginne noch heute, schneller zu entwickeln

Entdecke, was dein Team mit der intelligenten Orchestrierungsplattform für DevSecOps erreichen kann.