Eine private Docker-Registry mit S3 als Speicher betreiben
dockerregistry startet die offizielle Docker-Registry mit Login und S3 als Speicher, konfiguriert nur über Umgebungsvariablen. Wie du sie startest, was der kleine Go-Starter dabei schreibt und an welchen fünf Stellen du aufpassen solltest.

Wer eigene Docker-Images baut, braucht irgendwo eine Registry dafür. Die offizielle Registry aus dem Projekt distribution kann das, sie will aber eine Konfigurationsdatei und für den Login eine htpasswd-Datei mit bcrypt-Hashes. Beides musst du anlegen, bevor der Container startet.
dockerregistry nimmt mir diesen Schritt ab. Ein kleines Go-Programm liest beim Start die Umgebungsvariablen, schreibt daraus config.yml und htpasswd und startet dann die offizielle Registry in Version 2.8.3. Die Images landen in einem S3-Bucket, bei AWS oder einem kompatiblen Anbieter wie MinIO oder Cloudflare R2. Auf der Platte des Servers liegen keine Image-Daten. Der Text bezieht sich auf den Stand von main vom 11. Juli 2026 und auf das veröffentlichte Image 0.0.1.
Starten
Gegen einen echten S3-Bucket reicht ein docker run:
docker run -d --name registry -p 5000:5000 \
-e REGISTRY_USER=admin \
-e REGISTRY_PASSWORD='ein-langes-passwort' \
-e S3_BUCKET=my-registry \
-e S3_REGION=eu-central-1 \
-e S3_ACCESS_KEY=... \
-e S3_SECRET_KEY=... \
ghcr.io/danielgtmn/dockerregistry:latest
Danach meldest du dich an und schiebst ein Image hinein:
docker login localhost:5000
docker tag alpine:latest localhost:5000/alpine:latest
docker push localhost:5000/alpine:latest
Die Registry selbst spricht nur HTTP. Für den Zugriff von anderen Rechnern gehört ein Reverse Proxy mit TLS davor, etwa Caddy oder Traefik. Für einen anderen Anbieter als AWS setzt du zusätzlich S3_ENDPOINT. Dann schaltet der Starter automatisch auf Pfad-Adressen um (S3_FORCE_PATH_STYLE), weil MinIO und ähnliche Dienste das meist brauchen. Zum Ausprobieren liegt eine docker-compose.yml mit MinIO im Repository.
Was der Starter tut
Das Programm unter cmd/danielgtmn-dockerregistry arbeitet vier Schritte ab:
- Es liest die Konfiguration. Fehlt
S3_BUCKEToder ein Login, bricht es mit einer Meldung ab. Ohne Login startet die Registry also gar nicht. - Es hasht die Passwörter mit bcrypt und schreibt sie nach
/auth/htpasswd, Rechte0600. - Es schreibt
/etc/docker/registry/config.yml, ebenfalls mit0600. Darin stehen S3-Zugang, Login-Datei, Löschrecht und ein Gesundheitscheck für den Speicher alle zehn Sekunden. - Es entfernt seine eigenen Variablen wie
REGISTRY_USERaus der Umgebung und ersetzt sich perexecdurch/bin/registry serve. Das Entfernen ist nötig, weil die Registry jede Variable mitREGISTRY_am Anfang als Einstellung liest.
Mehrere Benutzer trägst du in REGISTRY_USERS ein, zum Beispiel ci:geheim,daniel:anderes.
Worauf du achten solltest
Das Image auf GHCR ist älter als der Code
Das Image 0.0.1, das auch als latest auf GHCR liegt, wurde aus dem Commit 0245f8c gebaut. Wenige Minuten später kam mit c487ead eine Änderung, die zwei Dinge behebt: Weiterleitungen zu S3 sind seitdem standardmäßig aus, und die eigenen Variablen werden vor dem Start der Registry entfernt. Im veröffentlichten Image steht in der Konfiguration fest redirect: disable: false.
Das fällt auf, sobald der Bucket für deine Clients nicht erreichbar ist, etwa bei einem MinIO im internen Netz. Dann schickt die Registry den Client per 307 an eine Adresse weiter, die er nicht erreicht, und der Push scheitert. So beschreibt es auch die Commit-Nachricht. Bis es ein neues Release gibt, baust du das Image selbst aus main:
git clone https://github.com/danielgtmn/dockerregistry
cd dockerregistry
docker build -t danielgtmn-dockerregistry .
Alle Daten laufen über die Registry
Seit c487ead sind Weiterleitungen auch gegen echtes AWS S3 aus. Jeder Pull geht dann durch deinen Server, und dessen Bandbreite ist die Grenze. Die distribution-Doku nennt das als sinnvolle Wahl, wenn der Speicher nicht in der Nähe liegt. Erreichen deine Clients den Bucket direkt, kannst du mit S3_REDIRECT=true wieder umschalten. In der Tabelle der README fehlt diese Variable, sie steht nur in der Hilfe des Programms.
Jeder Benutzer darf alles
Die Anmeldung kennt nur Benutzer und Passwort, keine Rollen. Wer sich anmelden kann, darf pushen, pullen und löschen, denn DELETE_ENABLED steht hier auf true. Die offizielle Registry hat das Löschen ohne Einstellung ausgeschaltet. Einzelnen Benutzern wie dem CI kannst du das Löschen nicht verbieten. Brauchst du es gar nicht, setz DELETE_ENABLED=false.
Bei REGISTRY_USERS trennt ein Komma die Einträge. Ein Komma im Passwort zerlegt den Eintrag, und der Start bricht mit „invalid REGISTRY_USERS entry“ ab. Ein Doppelpunkt im Passwort ist dagegen erlaubt, getrennt wird nur am ersten.
Löschen macht den Bucket nicht kleiner
Wenn du alte Tags entfernst, zum Beispiel mit docker-cleanup, löscht die Registry nur die Verweise. Die Schichten bleiben im Bucket liegen, bis eine Garbage Collection läuft. Das Image startet keine. Die Konfiguration liegt aber im Container, und einen Probelauf, der nichts löscht, startest du so:
docker exec registry registry garbage-collect --dry-run /etc/docker/registry/config.yml
Für den echten Lauf ohne --dry-run verlangt die distribution-Doku, dass die Registry schreibgeschützt ist oder gar nicht läuft. Sonst können Schichten eines Images, das gerade hochgeladen wird, mit gelöscht werden. Einen Schalter für den Schreibschutz hat dieses Image nicht. Plane den Lauf deshalb für eine Zeit, in der kein CI pusht.
Mehrere Instanzen brauchen ein gemeinsames Secret
Ohne REGISTRY_HTTP_SECRET erzeugt die Registry bei jedem Start ein zufälliges Secret. Damit signiert sie Zustandsdaten, die sie beim Client ablegt. Bei einer einzelnen Instanz ist das kein Problem. Laufen mehrere hinter einem Load Balancer, müssen sie laut Doku dasselbe Secret haben. Erzeug es einmal, etwa mit openssl rand -hex 32, und gib es allen Instanzen mit.
Wofür es reicht
dockerregistry ist eine dünne Hülle um die offizielle Registry. Für ein kleines Team oder die eigenen Projekte ist das genug: ein Container, ein Bucket, ein paar Logins. Wer Rollen, Weboberfläche oder Sicherheitsscans braucht, ist mit Harbor oder der Container Registry von GitLab besser bedient. Fragen und Fehler gehören in die Issues auf GitHub.