GitLab Container Registry: Images versionieren, aufraeumen und Zugriff steuern
AI generated
CI/CD
.yml
GitLab · Container Registry · DevOps
GitLab Container Registry
Images versionieren, aufraeumen, absichern

Die in GitLab eingebaute Container Registry spart einen externen Registry-Dienst, verlangt aber eine bewusste Tag-Strategie, automatische Cleanup-Policies gegen ausufernden Speicherverbrauch und klar definierte Zugriffsrechte pro Projekt und Gruppe.

16 Min. Lesezeit Container Registry Cleanup Policy Zugriffsrechte

1. Ein Registry-Dienst, der bereits im Projekt steckt

Jedes GitLab-Projekt bringt automatisch eine eigene Container Registry mit, sobald die Funktion in den Projekteinstellungen aktiviert ist. Das erspart Teams den Betrieb eines separaten Registry-Dienstes wie einer selbst gehosteten Harbor-Instanz oder eines externen Cloud-Registry-Abos, samt der dazugehoerigen Authentifizierung, Netzwerkkonfiguration und Wartung. Der CI-Runner ist ueber die vordefinierte Variable CI_REGISTRY bereits mit den noetigen Zugangsdaten ausgestattet, sodass ein Image direkt aus der Pipeline heraus gebaut und gepusht werden kann, ohne zusaetzliche Secrets manuell zu hinterlegen.

Diese enge Integration ist zugleich der groesste Vorteil und die groesste Gefahr: Weil das Pushen eines Images so reibungslos funktioniert, sammeln sich ueber Monate hinweg oft hunderte oder tausende Tags an, von denen die allermeisten nie wieder benoetigt werden. Ohne eine bewusste Strategie fuer Versionierung und Aufraeumen waechst die Registry unkontrolliert, was sowohl Speicherkosten als auch die Uebersichtlichkeit beim Suchen nach einem konkreten Image erheblich beeintraechtigt.

2. Eine Tag-Strategie, die Nachvollziehbarkeit sichert

Der haeufigste Anfaengerfehler ist, jedes Image nur mit latest zu taggen. Das funktioniert kurzfristig, macht es aber unmoeglich nachzuvollziehen, welcher Code-Stand tatsaechlich in einem laufenden Container steckt, sobald mehrfach neu gepusht wurde. Bewaehrt hat sich stattdessen eine Kombination aus mehreren Tags pro Build: ein unveraenderlicher Tag basierend auf der Commit-SHA fuer exakte Nachvollziehbarkeit, ein semantischer Versions-Tag fuer Releases und optional ein latest-Tag ausschliesslich fuer den jeweils neuesten Stand des Hauptbranches.

Wichtig ist, die Commit-SHA-Tags als die eigentliche Wahrheit zu behandeln und semantische oder latest-Tags lediglich als zusaetzliche, bewegliche Zeiger darauf zu verstehen. So laesst sich im Ernstfall exakt nachvollziehen, welches Image zu welchem Commit gehoert, was insbesondere bei Rollbacks unverzichtbar ist, wenn schnell auf einen bestimmten, bekannt funktionierenden Stand zurueckgewechselt werden muss.


# .gitlab-ci.yml
build-image:
  stage: build
  image: docker:27
  services:
    - docker:27-dind
  script:
    - docker login -u "$CI_REGISTRY_USER" -p "$CI_REGISTRY_PASSWORD" "$CI_REGISTRY"
    - docker build
        -t "$CI_REGISTRY_IMAGE:$CI_COMMIT_SHORT_SHA"
        -t "$CI_REGISTRY_IMAGE:$CI_COMMIT_REF_SLUG"
        .
    - docker push "$CI_REGISTRY_IMAGE:$CI_COMMIT_SHORT_SHA"
    - docker push "$CI_REGISTRY_IMAGE:$CI_COMMIT_REF_SLUG"
    - |
      if [ "$CI_COMMIT_TAG" ]; then
        docker tag "$CI_REGISTRY_IMAGE:$CI_COMMIT_SHORT_SHA" "$CI_REGISTRY_IMAGE:$CI_COMMIT_TAG"
        docker push "$CI_REGISTRY_IMAGE:$CI_COMMIT_TAG"
        docker tag "$CI_REGISTRY_IMAGE:$CI_COMMIT_SHORT_SHA" "$CI_REGISTRY_IMAGE:latest"
        docker push "$CI_REGISTRY_IMAGE:latest"
      fi

3. Cleanup-Policies: automatisches Aufraeumen statt manueller Pflege

GitLab bietet unter Settings > Packages and registries > Clean up image tags eine eingebaute Cleanup Policy, die regelmaessig laeuft (der genaue Zeitpunkt wird von GitLab intern geplant, nicht vom Nutzer festgelegt) und Tags anhand konfigurierbarer Regeln entfernt. Die zentralen Stellschrauben sind ein Regex-Muster fuer Tags, die aufbewahrt werden sollen, ein zweites Regex-Muster fuer Tags, die explizit von der Loeschung ausgenommen sind, sowie eine Regel, wie viele der neuesten Tags pro Muster in jedem Fall erhalten bleiben.

Eine sinnvolle Grundkonfiguration behaelt alle Tags, die wie eine semantische Version aussehen (^v?\d+\.\d+\.\d+$), unbegrenzt, waehrend Commit-SHA-Tags nur die letzten zwanzig bis dreissig Exemplare behalten und alle uebrigen, ungetaggten oder aelteren Tags nach einer definierten Frist geloescht werden. Diese Kombination stellt sicher, dass Release-Versionen dauerhaft nachvollziehbar bleiben, waehrend der Speicherverbrauch durch die deutlich zahlreicheren Zwischenbuild-Tags begrenzt bleibt.


# Cleanup-Policy per API konfigurieren
curl --request PUT --header "PRIVATE-TOKEN: <token>" \
  "https://gitlab.example.com/api/v4/projects/123" \
  --data-urlencode "container_expiration_policy_attributes[enabled]=true" \
  --data-urlencode "container_expiration_policy_attributes[cadence]=1d" \
  --data-urlencode "container_expiration_policy_attributes[keep_n]=25" \
  --data-urlencode "container_expiration_policy_attributes[older_than]=30d" \
  --data-urlencode "container_expiration_policy_attributes[name_regex_keep]=^v?[0-9]+\.[0-9]+\.[0-9]+$" \
  --data-urlencode "container_expiration_policy_attributes[name_regex]=.*"

4. Manuelles und geplantes Aufraeumen fuer Sonderfaelle

Die automatische Cleanup-Policy deckt den Regelfall ab, aber manche Situationen erfordern gezieltes manuelles Eingreifen, etwa wenn ein grosser Batch fehlerhafter Images aus einer defekten Pipeline-Konfiguration entfernt werden muss, bevor die naechste planmaessige Bereinigung laeuft. Ueber die Container Registry API lassen sich Tags gezielt per Skript loeschen, was sich gut in ein einmaliges Aufraeum-Skript oder einen dedizierten, manuell ausgeloesten CI-Job packen laesst.

Fuer wiederkehrende Sonderfaelle, etwa das woechentliche Entfernen aller Images von geloeschten Feature-Branches, bietet sich ein geplanter Pipeline-Job (Scheduled Pipeline) an, der ueber die API zunaechst alle noch existierenden Branches abfragt und anschliessend alle Registry-Tags loescht, die keinem aktiven Branch mehr zugeordnet werden koennen. Das ergaenzt die allgemeine Cleanup-Policy um eine gezielte, branchbezogene Bereinigungslogik.


# Gezieltes Loeschen eines einzelnen Tags per API
curl --request DELETE --header "PRIVATE-TOKEN: <token>" \
  "https://gitlab.example.com/api/v4/projects/123/registry/repositories/456/tags/feature-alter-branch"

# Bulk-Loeschung mit Namensfilter (asynchron, gibt eine Job-ID zurueck)
curl --request DELETE --header "PRIVATE-TOKEN: <token>" \
  "https://gitlab.example.com/api/v4/projects/123/registry/repositories/456/tags" \
  --data-urlencode "name_regex_delete=^feature-.*$" \
  --data-urlencode "keep_n=0"

5. Zugriffsrechte auf Projektebene steuern

Standardmaessig folgt der Zugriff auf die Container Registry der allgemeinen Sichtbarkeit des Projekts: Bei einem privaten Projekt benoetigen Nutzer mindestens Reporter-Zugriff, um Images zu pullen, und mindestens Developer-Zugriff, um zu pushen. Diese Kopplung ist fuer die meisten Teams sinnvoll, laesst sich aber unter Settings > General > Visibility, project features, permissions feiner steuern, etwa um die Registry unabhaengig von der allgemeinen Projektsichtbarkeit ausschliesslich fuer Projektmitglieder freizugeben.

Fuer CI/CD-Pipelines, die auf Images aus einem anderen Projekt zugreifen muessen (etwa ein zentrales Basis-Image-Repository), bietet sich ein Deploy Token mit dem Scope read_registry an, statt persoenliche Zugangsdaten in der Pipeline-Konfiguration zu hinterlegen. Deploy Tokens sind projektgebunden, laufzeitbeschraenkt konfigurierbar und koennen unabhaengig vom Konto einer einzelnen Person widerrufen werden, was ein deutlich saubereres Sicherheitsmodell als geteilte persoenliche Tokens ergibt.

6. Zugriffsrechte auf Gruppenebene und ueber Projektgrenzen hinweg

Fuer Organisationen mit mehreren zusammenhaengenden Projekten, etwa ein Basis-Image-Projekt und mehrere darauf aufbauende Anwendungsprojekte, lohnt sich eine gruppenweite Betrachtung. GitLab erlaubt es, ueber Group Deploy Tokens Zugriff auf die Registries aller Projekte innerhalb einer Gruppe zu gewaehren, was die Verwaltung einzelner Projekt-Tokens erheblich reduziert, wenn ein CI-Job aus Projekt B regelmaessig ein Basis-Image aus Projekt A ziehen muss.

Zusaetzlich unterstuetzt GitLab die CI_DEPENDENCY_PROXY-Variablen fuer den Dependency Proxy, der externe Registries wie Docker Hub gruppenweit cached und damit sowohl Rate-Limit-Probleme mit oeffentlichen Registries reduziert als auch eine zentrale Kontrolle darueber ermoeglicht, welche externen Basis-Images ueberhaupt in der Organisation verwendet werden duerfen. Diese Kombination aus eigener Registry und Dependency Proxy deckt die meisten Container-bezogenen Zugriffsszenarien ab, ohne einen externen Dienst zu benoetigen.


# Basis-Image ueber den Dependency Proxy statt direkt von Docker Hub ziehen
docker login -u "$CI_DEPENDENCY_PROXY_USER" \
  -p "$CI_DEPENDENCY_PROXY_PASSWORD" \
  "$CI_DEPENDENCY_PROXY_SERVER"

docker pull "$CI_DEPENDENCY_PROXY_GROUP_IMAGE_PREFIX/library/node:20-alpine"

7. Speicherverbrauch beobachten, bevor er zum Problem wird

Die Container Registry zaehlt bei GitLab.com direkt zum Speicherkontingent des Namespace, und auch bei selbst gehosteten Instanzen wirkt sich eine ungebremst wachsende Registry auf den Speicherplatz des Object-Storage-Backends aus. Unter Settings > Usage Quotas > Storage laesst sich der aktuelle Verbrauch der Registry getrennt von Repository- und Artefakt-Speicher einsehen, was fruehzeitig zeigt, ob die Cleanup-Policy tatsaechlich greift oder ob sich trotz aktivierter Regeln Tags unbemerkt ansammeln, etwa weil ein Regex-Muster versehentlich zu viele Tags von der Loeschung ausschliesst.

Ein regelmaessiger Blick auf diese Kennzahl, kombiniert mit einer kurzen Stichprobe der tatsaechlich vorhandenen Tags ueber die Registry-Oberflaeche, deckt Konfigurationsfehler meist innerhalb weniger Wochen auf, statt dass der Speicherverbrauch erst nach Monaten unkontrollierten Wachstums auffaellt. Besonders bei mehreren parallel aktiven Projekten mit eigener Registry lohnt sich ein monatlicher Sammel-Check aller Namespaces, um Ausreisser fruehzeitig zu identifizieren.

8. Image-Groesse optimieren, bevor Cleanup ueberhaupt noetig wird

Die effektivste Massnahme gegen eine ausufernde Registry setzt vor dem Aufraeumen an: kleinere Images verursachen weniger Speicherverbrauch pro Tag und werden schneller gepusht und gepullt. Multi-Stage-Builds trennen die Build-Umgebung mit Compiler, Composer-Cache und Dev-Abhaengigkeiten strikt vom schlanken Laufzeit-Image, sodass am Ende nur die tatsaechlich benoetigten Artefakte im finalen Layer landen. Fuer ein PHP-Projekt bedeutet das typischerweise, composer install --no-dev erst in der letzten Stage auszufuehren und den Vendor-Ordner aus einer vorherigen Build-Stage gezielt zu kopieren.

Zusaetzlich lohnt sich ein bewusster Umgang mit der Layer-Reihenfolge im Dockerfile: Selten aendernde Schichten wie das Betriebssystem-Basisimage und System-Pakete gehoeren an den Anfang, haeufig aendernder Anwendungscode ans Ende, damit der Docker-Build-Cache moeglichst viele Schichten wiederverwenden kann. Das reduziert nicht nur die Build-Zeit in der Pipeline, sondern haelt auch die Anzahl unterschiedlicher, tatsaechlich gespeicherter Layer-Kombinationen in der Registry kleiner.

9. Haeufige Fehler beim Betrieb der Container Registry

Ein wiederkehrender Fehler ist ein zu aggressives Regex-Muster in der Cleanup-Policy, das versehentlich auch semantische Versions-Tags erfasst und damit Release-Images loescht, die eigentlich dauerhaft erhalten bleiben sollten. Vor der produktiven Aktivierung einer neuen Cleanup-Regel empfiehlt sich daher ein Testlauf gegen eine kleine Stichprobe von Tags oder zumindest eine sorgfaeltige manuelle Pruefung des Regex-Musters gegen die tatsaechlich vorhandene Tag-Liste.

Ein zweiter haeufiger Fehler ist, persoenliche Zugangstoken statt Deploy Tokens fuer automatisierte Zugriffe zu verwenden. Verlaesst die zustaendige Person das Team oder wird ihr Konto deaktiviert, faellt der Zugriff fuer alle darauf angewiesenen Pipelines unerwartet aus. Die folgende Tabelle fasst die zentralen Mechanismen fuer Versionierung, Aufraeumen und Zugriffsschutz zusammen.

Mechanismus Zweck Empfohlene Konfiguration Ort in GitLab
Commit-SHA-Tag Exakte Nachvollziehbarkeit jedes Builds Immer zusaetzlich zu anderen Tags setzen CI-Job (docker build/push)
Cleanup Policy Automatisches Entfernen alter Tags Semantische Versionen behalten, SHA-Tags begrenzen Settings > Packages and registries
Deploy Token (read_registry) Pipeline-Zugriff ohne persoenliches Konto Fuer alle automatisierten Pulls verwenden Settings > Repository > Deploy tokens
Group Deploy Token Zugriff auf mehrere Projekt-Registries Bei zentralem Basis-Image-Projekt Gruppe > Settings > Deploy tokens
Dependency Proxy Caching externer Images, Rate-Limit-Schutz Fuer alle Docker-Hub-Basisimages Gruppe > Packages > Dependency Proxy

Mironsoft

CI/CD-Pipelines, Zero-Downtime-Deployments und Release-Automatisierung

Deployments, die ohne Ausfallzeit und ohne Nervenkitzel laufen?

Wir prüfen bestehende GitLab-Pipelines auf fragile Deployment-Schritte und fehlende Absicherung und bauen daraus einen Release-Prozess mit Zero-Downtime-Deployments, automatisierten Checks und einem Rollback, dem ihr im Ernstfall vertrauen könnt.

Pipeline-Review

Bestehende .gitlab-ci.yml auf Fragilität, fehlende Stages und Sicherheitslücken prüfen.

Zero-Downtime-Deployment

Symlink-Releases, Health-Checks und Rollback-Strategien für Magento-Shops aufbauen.

CI/CD-Automatisierung

Tests, Security-Scans und Deployments zu einer zuverlässigen Pipeline verbinden.

10. Zusammenfassung

Container Registry: Das Wichtigste auf einen Blick

Tag-Strategie

Commit-SHA als Wahrheit, semantische Versionen und latest als bewegliche Zeiger.

Cleanup Policy

Automatisches Aufraeumen mit Regex-Ausnahmen fuer Release-Versionen.

Deploy Tokens

Projektgebundener, widerrufbarer Zugriff statt persoenlicher Zugangsdaten.

Dependency Proxy

Externe Basis-Images gruppenweit cachen statt direkt von Docker Hub ziehen.

11. FAQ: Container Registry: Das Wichtigste auf einen Blick

1Wie oft laeuft die Cleanup Policy standardmaessig?
Die genaue Ausfuehrungshaeufigkeit wird von GitLab intern geplant und ist nicht exakt vom Nutzer steuerbar, laesst sich aber ueber das Feld 'cadence' auf taeglich, woechentlich, alle zwei Wochen oder monatlich einstellen.
2Kann ich ein geloeschtes Image wiederherstellen?
Nein, das Loeschen eines Tags in der Container Registry ist nicht ueber die GitLab-Oberflaeche rueckgaengig zu machen. Falls ein Backup-Mechanismus fuer das Object-Storage-Backend existiert, ist eine Wiederherstellung theoretisch moeglich, aber nicht Teil der Standardfunktionalitaet.
3Was ist der Unterschied zwischen einem Deploy Token und einem Personal Access Token fuer die Registry?
Ein Deploy Token ist projektgebunden und unabhaengig von einem einzelnen Nutzerkonto, waehrend ein Personal Access Token an eine bestimmte Person gebunden ist und bei deren Ausscheiden oder Kontodeaktivierung ungueltig wird.
4Warum werden manche Tags trotz aktiver Cleanup Policy nicht geloescht?
Haeufigste Ursache ist ein zu weit gefasstes name_regex_keep-Muster oder eine zu hohe keep_n-Zahl, die mehr Tags erhaelt als beabsichtigt. Ein gezielter Testlauf des Regex-Musters gegen die vorhandene Tag-Liste deckt solche Fehler meist schnell auf.
5Kann ich die Container Registry komplett deaktivieren, wenn ich sie nicht nutze?
Ja, unter Settings > General > Visibility, project features, permissions kann die Registry-Funktion pro Projekt vollstaendig deaktiviert werden, was sie auch aus der Projektnavigation entfernt.
6Wie unterscheidet sich der Dependency Proxy von der Container Registry?
Die Container Registry speichert eigene, selbst gebaute Images, waehrend der Dependency Proxy externe Images von Registries wie Docker Hub zwischenspeichert, um Rate Limits zu vermeiden und wiederholte externe Downloads zu reduzieren.
7Zaehlt der Dependency-Proxy-Cache zum Speicherkontingent des Projekts?
Ja, gecachte Images ueber den Dependency Proxy zaehlen ebenfalls zum Speicherverbrauch der Gruppe und sollten bei der Beobachtung des Gesamtspeichers beruecksichtigt werden.
8Kann ich mehrere Cleanup-Regeln fuer unterschiedliche Tag-Muster kombinieren?
Die eingebaute Cleanup Policy erlaubt pro Projekt nur ein Regelset mit einem Keep- und einem Delete-Muster. Fuer komplexere, mehrstufige Regeln ist ein eigenes Skript ueber die Registry API notwendig.
9Wie pruefe ich, welches Image aktuell in Produktion laeuft?
Am zuverlaessigsten ueber das Commit-SHA-Tag, das beim Deployment mitgeloggt werden sollte, etwa als Umgebungsvariable im laufenden Container oder als Eintrag im Deployment-Log der Pipeline.
10Sollte ich latest-Tags in Produktion verwenden?
Nein, latest-Tags sind ein beweglicher Zeiger und eignen sich nicht fuer reproduzierbare Deployments. Fuer Produktionsumgebungen sollte immer ein unveraenderlicher Tag wie die Commit-SHA oder eine feste semantische Version referenziert werden.