Registry Garbage Collection: Wie eigene Docker-Registries Speicher wirklich freigeben
AI generated
FROM
RUN
Docker · Registry · Betrieb
Registry Garbage Collection
Speicherwachstum eigener Registries kontrollieren

docker system prune raeumt nur lokal auf, das Wachstum einer eigenen Docker Registry mit Tausenden gepushten Images bleibt davon unberuehrt. Registry-seitige Garbage Collection und durchdachte Retention-Policies sind der eigentliche Hebel gegen ausufernden Speicherverbrauch.

17 Min. Lesezeit Docker Registry Betrieb

1. Warum docker system prune hier nicht hilft

Ein weit verbreitetes Missverstaendnis ist, docker system prune oder docker image prune wuerden auch den Speicherplatz auf dem Registry-Server reduzieren. Tatsaechlich wirken diese Befehle ausschliesslich lokal auf dem Rechner oder Runner, auf dem sie ausgefuehrt werden, und raeumen dort ungenutzte Images, gestoppte Container und verwaiste Layer auf. Der Speicher einer selbst betriebenen Registry, etwa auf Basis von Docker Distribution, Harbor oder GitLab Container Registry, bleibt davon vollstaendig unberuehrt, denn die Registry ist ein eigenstaendiger Dienst mit eigenem Speicher-Backend, meist einem Objektspeicher wie S3 oder einem lokalen Dateisystem.

In einer aktiven CI/CD-Pipeline mit taeglich mehreren Builds, jeweils versehen mit eindeutigen Git-SHA- oder Datums-Tags wie im vorherigen Artikel beschrieben, wird eine Registry ohne eigene Aufraeum-Strategie kontinuierlich groesser, selbst wenn die allermeisten dieser Images nach kurzer Zeit nie wieder abgerufen werden. Ohne Gegenmassnahme fuehrt das langfristig zu stark steigenden Speicherkosten, laengeren Backup-Zeiten und im Extremfall zu vollgelaufenen Storage-Volumes, die den Registry-Betrieb komplett zum Erliegen bringen koennen.

2. Wie eine Registry Images intern speichert

Eine Docker Registry speichert Images nicht als monolithische Dateien, sondern als Menge von Content-adressierten Blobs, die den einzelnen Layern und der Manifest-Datei entsprechen, plus einer Zuordnungstabelle, die Tags auf Manifest-Digests abbildet. Mehrere Tags oder sogar mehrere unterschiedliche Images koennen sich denselben Blob teilen, etwa eine gemeinsame Basis-Layer wie php:8.4-fpm-alpine, wodurch die Registry effizient nur einmal Speicherplatz fuer identische Inhalte belegt, unabhaengig davon, wie viele Tags letztlich darauf verweisen.

Genau diese Struktur macht das Loeschen kompliziert: Ein einfaches Entfernen eines Tags loescht in der Regel nur die Zuordnung im Manifest-Index, nicht aber die zugrunde liegenden Blobs, denn die Registry kann zum Zeitpunkt des Tag-Loeschens nicht ohne Weiteres wissen, ob ein anderer Tag oder ein anderes Image denselben Blob noch referenziert. Genau hier setzt die eigentliche Garbage Collection an, die in einem separaten Durchlauf alle noch referenzierten Blobs ermittelt und ausschliesslich die tatsaechlich verwaisten Blobs physisch von der Platte entfernt.

3. Garbage Collection bei Docker Distribution im Detail

Die freie Referenzimplementierung Docker Distribution, auf der viele selbst gehostete Registries basieren, bringt einen eingebauten Garbage-Collector mit, der ueber den Befehl registry garbage-collect innerhalb des Registry-Containers aufgerufen wird. Der Ablauf gliedert sich in zwei Phasen: Zunaechst wird ein Mark-Durchlauf ausgefuehrt, der ausgehend von allen noch existierenden Tags rekursiv alle referenzierten Manifest- und Layer-Digests markiert, danach ein Sweep-Durchlauf, der alle nicht markierten Blobs physisch loescht.

Wichtig ist, dass die Registry waehrend des Garbage-Collection-Laufs im schreibgeschuetzten Modus betrieben werden sollte, sofern nicht die neuere Version mit --dry-run und Delete-Marker-Unterstuetzung verwendet wird, da sonst parallel gepushte Images faelschlicherweise als verwaist markiert und geloescht werden koennten. In produktiven Setups wird der GC-Lauf daher meist in einem Wartungsfenster oder mit der neueren Delete-Marker-Strategie ausgefuehrt, die ein gefahrloses Loeschen auch bei laufendem Registry-Betrieb erlaubt.


# Garbage Collection bei Docker Distribution (klassischer Ablauf)
docker exec registry bin/registry garbage-collect \
  --dry-run /etc/docker/registry/config.yml

# Nach Pruefung des Dry-Run tatsaechlich loeschen
docker exec registry bin/registry garbage-collect \
  /etc/docker/registry/config.yml

4. GC-Performance und Laufzeit bei grossen Registries

Bei einer kleinen internen Registry mit wenigen hundert Images dauert ein Garbage-Collection-Lauf meist nur Sekunden bis wenige Minuten, doch bei grossen Registries mit Zehntausenden Repositories und Millionen Blobs kann der Mark-Durchlauf, der jedes Manifest einzeln aufloesen und rekursiv durchlaufen muss, mehrere Stunden in Anspruch nehmen. Wird die GC in dieser Zeit im schreibgeschuetzten Modus betrieben, bedeutet das faktisch einen mehrstuendigen Ausfall aller Push-Operationen, was fuer aktive CI/CD-Landschaften kaum akzeptabel ist.

Als Gegenmassnahme setzen groessere Deployments meist auf die Delete-Marker-Strategie, die Loeschungen zunaechst nur als Markierung vormerkt und den eigentlichen physischen Sweep in einem separaten, klar terminierten Wartungsfenster ausfuehrt, sowie auf ein Objektspeicher-Backend wie S3 statt eines lokalen Dateisystems, da Objektspeicher-APIs den parallelen, batch-weisen Zugriff auf Millionen Blobs deutlich effizienter unterstuetzen als ein einzelnes lokales Dateisystem mit entsprechend vielen Inodes.

5. Retention-Policies fuer alte Tags einrichten

Garbage Collection allein loescht nur bereits entfernte Tags physisch, sie entscheidet aber nicht, welche Tags ueberhaupt geloescht werden sollen. Diese Aufgabe uebernimmt eine Retention-Policy, die Regeln definiert wie: behalte die letzten zehn Tags pro Repository, loesche Tags aelter als neunzig Tage ausser den letzten fuenf SemVer-Releases, oder behalte alle Tags, die mit einem geschuetzten Praefix wie release- beginnen. Ohne eine solche Policy waechst die Menge an Tags unbegrenzt weiter, selbst wenn die zugrunde liegenden Blobs durch Deduplizierung teilweise geteilt werden.

In Registries wie Harbor oder GitLab Container Registry lassen sich Retention-Policies deklarativ ueber die Weboberflaeche oder eine YAML-Konfiguration definieren und laufen automatisch nach einem festen Zeitplan, meist taeglich oder woechentlich. Bei der reinen Docker-Distribution-Registry ohne Zusatzsoftware muss diese Logik hingegen selbst per Skript implementiert werden, das ueber die Registry-HTTP-API alle Tags eines Repositories abfragt, die Regeln anwendet und ueberzaehlige Tags gezielt loescht, bevor im Anschluss der eigentliche Garbage-Collection-Lauf gestartet wird.


# Harbor Tag-Retention-Regel (Beispiel-Konfiguration)
rules:
  - repoMatches: "mironsoft/app"
    tagMatches: "sha-*"
    retention: "keep the most recently pushed 20 tags"
  - repoMatches: "mironsoft/app"
    tagMatches: "release-*"
    retention: "keep forever"

6. Lifecycle-Policies bei Cloud-Registries

Cloud-Registries wie AWS ECR, Google Artifact Registry oder Azure Container Registry bringen eigene Lifecycle-Policy-Mechanismen mit, die konzeptionell dieselbe Aufgabe erfuellen wie die Retention-Policies bei Harbor, jedoch anbieterspezifisch konfiguriert werden. Bei ECR etwa definiert man eine Lifecycle-Policy als JSON-Regelwerk mit Prioritaeten, das zum Beispiel untagged Images nach einer bestimmten Anzahl Tage automatisch entfernt und die Anzahl gespeicherter Images pro Tag-Praefix begrenzt.

Der entscheidende Vorteil dieser verwalteten Loesungen ist, dass die eigentliche Garbage Collection, also das physische Freigeben des Speichers nach der Loeschregel-Anwendung, vom Cloud-Anbieter automatisch und ohne manuellen Wartungsaufwand durchgefuehrt wird. Bei selbst gehosteten Registries hingegen muss der Betreiber sowohl die Retention-Policy als auch den eigentlichen Garbage-Collection-Lauf selbst orchestrieren, meist per Cron-Job oder als periodischer CI-Pipeline-Schritt ausserhalb der eigentlichen Anwendungs-Pipelines.


# AWS ECR Lifecycle-Policy anwenden
aws ecr put-lifecycle-policy \
  --repository-name mironsoft/app \
  --lifecycle-policy-text '{
    "rules": [{
      "rulePriority": 1,
      "description": "Untagged Images nach 7 Tagen entfernen",
      "selection": {"tagStatus": "untagged", "countType": "sinceImagePushed", "countUnit": "days", "countNumber": 7},
      "action": {"type": "expire"}
    }]
  }'

7. Der Sonderfall ungetaggter Manifeste

Ein haeufig uebersehenes Detail ist, dass beim Ueberschreiben eines Tags, etwa wenn app:buildcache bei jedem CI-Lauf neu gepusht wird wie im Artikel zu BuildKit Remote Cache beschrieben, das vorherige Manifest nicht automatisch geloescht wird, sondern als sogenanntes ungetaggtes oder dangling Manifest in der Registry verbleibt, bis es explizit entfernt oder von der Garbage Collection als verwaist erkannt wird. Bei haeufig wechselnden Cache-Tags oder CI-Tags kann sich dadurch eine erhebliche Menge ungetaggter, aber noch referenzierter oder erst nach GC tatsaechlich entfernbarer Manifeste ansammeln.

Fuer diesen Fall bieten die meisten Registry-APIs einen Endpunkt, um alle Manifest-Digests eines Repositories aufzulisten und mit der Menge der aktuell vergebenen Tags abzugleichen, sodass ungetaggte Manifeste gezielt identifiziert und vor dem naechsten Garbage-Collection-Lauf explizit per DELETE-Request entfernt werden koennen. Diese explizite Bereinigung beschleunigt den nachfolgenden GC-Lauf, da weniger Manifeste im Mark-Durchlauf betrachtet werden muessen, und macht das Speicherwachstum insgesamt vorhersagbarer.

8. Speicherwachstum ueberwachen

Ohne Monitoring bleibt Speicherwachstum meist unbemerkt, bis das zugrunde liegende Volume tatsaechlich vollgelaufen ist und Pushes fehlschlagen, was in einer aktiven CI/CD-Pipeline zu einem kompletten Ausfall aller Deploys fuehren kann. Sinnvoll ist daher, den belegten Speicherplatz des Registry-Backends regelmaessig zu erfassen, etwa ueber CloudWatch-Metriken bei ECR, den Storage-Endpunkt bei Harbor, oder ein eigenes Skript, das die Groesse des zugrunde liegenden S3-Buckets oder Dateisystems misst und als Metrik an Prometheus oder ein aequivalentes System exportiert.

Zusaetzlich zum reinen Speicherverbrauch lohnt sich ein Blick auf die Anzahl der Tags pro Repository und deren Zuwachsrate ueber die Zeit, denn ein ploetzlicher Anstieg kann auf eine fehlerhafte CI-Konfiguration hindeuten, etwa eine Pipeline, die versehentlich bei jedem Lauf einen neuen, eindeutigen Tag statt eines wiederverwendeten Cache-Tags erzeugt. Ein Alert bei ueberschrittenem Schwellenwert fuer Speicherverbrauch oder Tag-Anzahl verschafft dem Betriebsteam ausreichend Vorlaufzeit, um Retention-Policies nachzujustieren, bevor es kritisch wird.

9. Best Practices fuer nachhaltigen Registry-Betrieb

In der Praxis bewaehrt sich eine Kombination aus mehreren Massnahmen: eine klare Tagging-Strategie wie im vorherigen Artikel beschrieben, die zwischen dauerhaft zu behaltenden Release-Tags und kurzlebigen CI-Tags unterscheidet, eine automatisierte Retention-Policy, die kurzlebige Tags nach einer definierten Frist entfernt, ein regelmaessiger, idealerweise woechentlicher Garbage-Collection-Lauf im Wartungsfenster oder mit Delete-Marker-Unterstuetzung im laufenden Betrieb, sowie kontinuierliches Monitoring des Speicherverbrauchs mit Alarmierung bei Anomalien.

Wer diese Bausteine von Anfang an einplant, statt sie erst nach einem vollgelaufenen Storage-Volume nachzuruesten, vermeidet nicht nur unnoetige Betriebskosten, sondern auch akute Ausfaelle der Registry, die im schlimmsten Fall die gesamte Deployment-Faehigkeit eines Teams blockieren koennen. Die folgende Tabelle vergleicht die wichtigsten Aufraeum-Mechanismen und ihre jeweilige Rolle.

Mechanismus Was wird entfernt Ausloeser Typisches Werkzeug
docker system prune Lokale, ungenutzte Images/Container Manuell auf Host/Runner Docker CLI
Retention-Policy Ueberzaehlige oder veraltete Tags Zeitplan oder Regelwerk Harbor, ECR, GitLab Registry
Garbage Collection Physisch verwaiste Blobs Nach Tag-Loeschung, periodisch registry garbage-collect, Cloud-GC
Manuelle Manifest-Bereinigung Ungetaggte, dangling Manifeste Ad-hoc oder vor GC-Lauf Registry-HTTP-API, eigenes Skript

Mironsoft

Container-Infrastruktur, CI-Pipelines und Deployment-Automatisierung

Docker-Setups, die im Team und in Produktion tragfähig bleiben?

Wir prüfen bestehende Dockerfiles und Compose-Stacks auf Sicherheitslücken, aufgeblähte Images und fragile Build-Pipelines und bauen daraus eine Container-Infrastruktur, die schnell baut, sicher läuft und im Team nachvollziehbar bleibt.

Dockerfile-Review

Multi-Stage-Builds, Layer-Caching und Image-Größe systematisch optimieren.

Security-Audit

Container-Isolation, Secrets-Handling und Image-Scanning gegen echte Angriffsflächen absichern.

CI/CD-Integration

Build-Pipelines, Registries und Deployment-Strategien für reproduzierbare Releases aufbauen.

10. Zusammenfassung

Registry Garbage Collection: Das Wichtigste auf einen Blick

Kernproblem

docker system prune wirkt nur lokal, das Speicherwachstum der Registry selbst bleibt davon unberuehrt.

Zwei Schritte noetig

Erst Retention-Policy entscheidet, welche Tags weg sollen, dann Garbage Collection gibt den Speicher physisch frei.

Cloud vs. selbst gehostet

Cloud-Registries automatisieren GC, selbst gehostete Registries brauchen eigene Cron-Jobs dafuer.

Monitoring wichtig

Speicherverbrauch und Tag-Anzahl ueberwachen, um Fehlkonfigurationen fruehzeitig zu erkennen.

11. FAQ: Registry Garbage Collection: Das Wichtigste auf einen Blick

1Reicht docker system prune, um eine Registry aufzuraeumen?
Nein, docker system prune wirkt ausschliesslich lokal auf dem Host oder Runner, auf dem es ausgefuehrt wird. Der Speicher der Registry selbst, meist ein Objektspeicher oder Dateisystem auf dem Registry-Server, bleibt davon vollstaendig unberuehrt.
2Was passiert, wenn ich einen Tag in der Registry loesche?
In der Regel wird nur die Zuordnung des Tags zum Manifest entfernt, nicht aber die zugrunde liegenden Blobs. Diese werden erst durch einen separaten Garbage-Collection-Lauf physisch freigegeben, sofern kein anderer Tag mehr darauf verweist.
3Wie funktioniert Garbage Collection bei Docker Distribution?
In zwei Phasen: Ein Mark-Durchlauf markiert alle noch von Tags referenzierten Manifest- und Layer-Digests, ein anschliessender Sweep-Durchlauf loescht physisch alle nicht markierten, also verwaisten Blobs.
4Muss die Registry waehrend der Garbage Collection offline sein?
Bei der klassischen Docker-Distribution-Implementierung sollte die Registry schreibgeschuetzt sein, um Race Conditions zu vermeiden. Neuere Versionen mit Delete-Marker-Unterstuetzung erlauben Garbage Collection auch im laufenden Betrieb.
5Was ist eine Retention-Policy?
Eine Retention-Policy ist ein Regelwerk, das festlegt, welche Tags behalten und welche geloescht werden sollen, etwa die letzten zehn Tags pro Repository oder alle Tags aelter als neunzig Tage ausser markierten Releases.
6Automatisieren Cloud-Registries die Garbage Collection?
Ja, bei AWS ECR, Google Artifact Registry oder Azure Container Registry uebernimmt der Anbieter die eigentliche physische Speicherfreigabe automatisch, sobald eine Lifecycle-Policy die zu loeschenden Images bestimmt hat.
7Was ist ein ungetaggtes oder dangling Manifest?
Ein Manifest, das durch Ueberschreiben eines Tags nicht mehr von einem Tag referenziert wird, aber noch nicht durch Garbage Collection entfernt wurde. Es belegt weiterhin Speicherplatz, bis es explizit bereinigt oder von der GC erkannt wird.
8Wie oft sollte Garbage Collection laufen?
Ein woechentlicher Rhythmus hat sich in der Praxis bei aktiven CI/CD-Pipelines bewaehrt. Bei sehr hoher Push-Frequenz kann ein taeglicher Lauf sinnvoll sein, um Speicherwachstum schneller einzudaemmen.
9Wie erkenne ich, ob meine Registry ein Speicherproblem hat?
Durch Monitoring des belegten Speicherplatzes des Registry-Backends sowie der Tag-Anzahl pro Repository ueber die Zeit. Ein ploetzlicher, unerwarteter Anstieg deutet meist auf eine fehlerhafte CI-Konfiguration hin.
10Kann ich Release-Tags vor der Garbage Collection schuetzen?
Ja, ueber Retention-Policies mit Ausnahmeregeln fuer bestimmte Tag-Praefixe wie release- oder v*, die dauerhaft behalten werden, waehrend kurzlebige CI-Tags nach einer definierten Frist geloescht werden.