Distribution (anciennement docker/distribution) est l'implémentation de référence de la spécification OCI Distribution. C'est un registry de conteneurs minimaliste : il stocke et distribue des images OCI/Docker, sans interface web, sans RBAC avancé, sans scan de vulnérabilités. C'est le socle sur lequel sont construits Docker Hub, Harbor, et la plupart des registries du marché.
À utiliser quand les besoins sont simples : un registry privé pour une équipe, un registry interne pour un cluster isolé, ou comme brique de base dans une architecture custom.
Idéal pour : registry privé léger sans infrastructure complexe, environnements air-gapped, laboratoires et CI/CD internes, socle d'une solution custom.
Informations essentielles
Origine : Docker → CNCF (distribution/distribution) · Licence : Apache 2.0 · Architectures : x86_64, ARM64, ARM
Liens : GitHub · Releases · Image Docker
Support : Projet actif. La v3.x est la branche stable depuis avril 2026 (v3.0.0 GA), avec nettoyage du code legacy. L'image Docker registry:2 (v2.8.x) reste très répandue en production.
Backends de stockage
| Backend | Usage |
|---|---|
| filesystem | Stockage local (défaut) |
| S3 / S3-compatible (MinIO) | Cloud ou on-prem objet |
| Google Cloud Storage | GCP |
| Azure Blob Storage | Azure |
| Swift | OpenStack |
Démarrage rapide (Docker)
# Registry local sur le port 5000 (sans TLS, dev uniquement)
docker run -d \
--name registry \
-p 5000:5000 \
-v /data/registry:/var/lib/registry \
registry:2
# Pousser une image
docker tag nginx:alpine localhost:5000/nginx:alpine
docker push localhost:5000/nginx:alpine
# Vérifier via l'API
curl http://localhost:5000/v2/_catalog
# {"repositories":["nginx"]}
curl http://localhost:5000/v2/nginx/tags/list
# {"name":"nginx","tags":["alpine"]}
Sans TLS, Docker refuse de pousser vers des registries non-localhost par défaut. Ajouter le registry en
insecure-registriesdans/etc/docker/daemon.jsonpour les IPs non-locales (voir Configuration avancée).
Déploiement avec TLS et authentification
1. Générer les credentials htpasswd
# Créer un fichier htpasswd (bcrypt obligatoire)
docker run --rm \
--entrypoint htpasswd \
httpd:2 -Bbn monuser monpassword > auth/htpasswd
2. Configurer avec TLS
# Structure
mkdir -p /data/registry/{data,auth,certs}
# Certificat TLS (Let's Encrypt ou self-signed)
# Placer registry.crt et registry.key dans /data/registry/certs/
# Lancer avec TLS + auth
docker run -d \
--name registry \
-p 443:443 \
-v /data/registry/data:/var/lib/registry \
-v /data/registry/auth:/auth \
-v /data/registry/certs:/certs \
-e REGISTRY_HTTP_ADDR=0.0.0.0:443 \
-e REGISTRY_HTTP_TLS_CERTIFICATE=/certs/registry.crt \
-e REGISTRY_HTTP_TLS_KEY=/certs/registry.key \
-e REGISTRY_AUTH=htpasswd \
-e REGISTRY_AUTH_HTPASSWD_REALM="Registry Realm" \
-e REGISTRY_AUTH_HTPASSWD_PATH=/auth/htpasswd \
registry:2
Configuration via fichier YAML
Pour les configurations complexes, un fichier config.yml est plus lisible que les variables d'environnement.
# /etc/distribution/config.yml
version: 0.1
log:
level: info
storage:
filesystem:
rootdirectory: /var/lib/registry
# Ou backend S3 :
# s3:
# region: eu-west-1
# bucket: mon-registry
# encrypt: true
# secure: true
delete:
enabled: true # Permet la suppression d'images via API
http:
addr: 0.0.0.0:5000
tls:
certificate: /certs/registry.crt
key: /certs/registry.key
auth:
htpasswd:
realm: "Registry"
path: /auth/htpasswd
docker run -d \
--name registry \
-p 5000:5000 \
-v /etc/distribution/config.yml:/etc/distribution/config.yml \
-v /var/lib/registry:/var/lib/registry \
registry:2 /etc/distribution/config.yml
Backend S3 (MinIO ou AWS S3)
storage:
s3:
region: us-east-1
bucket: mon-registry-bucket
regionendpoint: http://minio.example.com:9000 # Omit pour AWS S3
accesskey: AKIAIOSFODNN7EXAMPLE
secretkey: wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY
encrypt: false
secure: true
v4auth: true
delete:
enabled: true
Déploiement sur Kubernetes
# registry-deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: registry
namespace: registry
spec:
replicas: 1
selector:
matchLabels:
app: registry
template:
spec:
containers:
- name: registry
image: registry:2
ports:
- containerPort: 5000
env:
- name: REGISTRY_STORAGE_DELETE_ENABLED
value: "true"
- name: REGISTRY_AUTH
value: htpasswd
- name: REGISTRY_AUTH_HTPASSWD_PATH
value: /auth/htpasswd
- name: REGISTRY_AUTH_HTPASSWD_REALM
value: "Registry"
volumeMounts:
- name: registry-data
mountPath: /var/lib/registry
- name: auth
mountPath: /auth
volumes:
- name: registry-data
persistentVolumeClaim:
claimName: registry-pvc
- name: auth
secret:
secretName: registry-auth
---
apiVersion: v1
kind: Service
metadata:
name: registry
namespace: registry
spec:
selector:
app: registry
ports:
- port: 5000
Registries insecure (sans TLS)
Pour autoriser Docker/containerd à utiliser un registry sans TLS ou avec un certificat self-signed :
// /etc/docker/daemon.json
{
"insecure-registries": ["192.168.1.100:5000", "registry.interne:5000"]
}
# /etc/containerd/config.toml - pour Kubernetes
[plugins."io.containerd.grpc.v1.cri".registry.mirrors."192.168.1.100:5000"]
endpoint = ["http://192.168.1.100:5000"]
[plugins."io.containerd.grpc.v1.cri".registry.configs."192.168.1.100:5000".tls]
insecure_skip_verify = true
Garbage collection
Distribution ne supprime pas automatiquement les layers orphelins. La garbage collection doit être lancée manuellement.
# Exécuter la GC (en mode dry-run d'abord)
docker exec registry registry garbage-collect --dry-run /etc/distribution/config.yml
# GC effective
docker exec registry registry garbage-collect /etc/distribution/config.yml
# Note : arrêter les writes pendant la GC pour éviter la corruption
docker stop registry
docker run --rm \
-v /data/registry/data:/var/lib/registry \
registry:2 garbage-collect /etc/distribution/config.yml
docker start registry
API OCI Distribution Spec
Distribution implémente l'API standard, utilisable directement :
# Lister les repositories
curl -u user:pass https://registry.example.com/v2/_catalog
# Lister les tags d'une image
curl -u user:pass https://registry.example.com/v2/<image>/tags/list
# Récupérer le manifest d'une image
curl -u user:pass \
-H "Accept: application/vnd.oci.image.manifest.v1+json" \
https://registry.example.com/v2/<image>/manifests/<tag>
# Supprimer un tag (DELETE, nécessite storage.delete.enabled: true)
DIGEST=$(curl -I -u user:pass \
-H "Accept: application/vnd.docker.distribution.manifest.v2+json" \
https://registry.example.com/v2/<image>/manifests/<tag> \
| grep -i docker-content-digest | awk '{print $2}' | tr -d '\r')
curl -u user:pass -X DELETE \
https://registry.example.com/v2/<image>/manifests/$DIGEST
Mise à jour
# Arrêter et remplacer le conteneur (les données sont dans le volume)
docker stop registry
docker rm registry
docker pull registry:2
docker run -d --name registry [... mêmes options ...]
Commandes utiles
# Vérifier que le registry répond
curl https://registry.example.com/v2/
# Lister les images
curl -u user:pass https://registry.example.com/v2/_catalog
# Lister les tags
curl -u user:pass https://registry.example.com/v2/<image>/tags/list
# Inspecter les logs
docker logs registry -f
# Espace utilisé
du -sh /data/registry/data
Ressources
- GitHub : https://github.com/distribution/distribution
- OCI Distribution Spec : https://github.com/opencontainers/distribution-spec
- Image Docker officielle : https://hub.docker.com/_/registry
- Configuration reference : https://distribution.github.io/distribution/about/configuration/