Un registry mirror funciona como una caché intermedia entre Docker Engine y Docker Hub. La primera vez que un cliente solicita una imagen, el mirror descarga sus capas desde Docker Hub, las almacena y las entrega al cliente. En solicitudes posteriores, las capas disponibles se sirven desde el almacenamiento local.

Esto reduce descargas repetidas y mejora el tiempo de aprovisionamiento cuando varios servidores utilizan las mismas imágenes. No convierte el servicio en un registry privado: solo actúa como caché de Docker Hub y no admite operaciones docker push. Tampoco puede utilizarse para replicar otros registries privados. Docker Docs, CNCF Distribution.
Arquitectura
La solución tendrá dos componentes:
- Un servidor mirror con Distribution Registry y almacenamiento persistente.
- Uno o más clientes Docker Engine configurados para consultar
https://mirror.ejemplo.com.
Aunque una imagen esté almacenada en caché, una solicitud basada en una etiqueta como latest puede consultar Docker Hub para comprobar si existe una versión nueva. Las capas que no cambiaron se sirven localmente.
Requisitos previos
- Un servidor Ubuntu 24.04 LTS con Docker Engine y el complemento Docker Compose.
- Un dominio, por ejemplo
mirror.ejemplo.com, apuntando al servidor. - Un certificado TLS válido para ese dominio.
- Puerto TCP 443 accesible desde los clientes autorizados.
- Espacio en disco suficiente para
/srv/registry-mirror/data. - Acceso mediante
sudotanto al mirror como a los clientes.
En producción, limita el acceso mediante la red del proveedor, una VPN o reglas en la cadena DOCKER-USER. Los puertos publicados por Docker pueden no respetar como se espera las reglas habituales de UFW. Instalación de Docker Engine en Ubuntu.
Paso 1: registrar el estado actual
En el servidor mirror y en cada cliente, registra las versiones antes de modificar la configuración:
docker version
docker info
sudo systemctl is-active dockerConfirma que Docker esté activo y resuelve cualquier error previo antes de continuar.
Paso 2: preparar el servidor mirror
Crea las rutas de configuración, certificados y datos:
sudo install -d -m 0750 \
/srv/registry-mirror/data \
/srv/registry-mirror/certs
cd /srv/registry-mirrorCopia el certificado y la clave privada como certs/fullchain.pem y certs/privkey.pem. La clave debe quedar restringida:
sudo chmod 0644 certs/fullchain.pem
sudo chmod 0600 certs/privkey.pemNo expongas la clave privada en capturas, repositorios ni historiales de comandos.
Paso 3: configurar el pull-through cache
Crea /srv/registry-mirror/config.yml:
version: 0.1
log:
level: info
storage:
delete:
enabled: true
filesystem:
rootdirectory: /var/lib/registry
http:
addr: 0.0.0.0:5000
tls:
certificate: /certs/fullchain.pem
key: /certs/privkey.pem
proxy:
remoteurl: https://registry-1.docker.io
ttl: 168hEl backend filesystem es el recomendado para garantizar el funcionamiento correcto de la caché. delete.enabled permite que el programador elimine contenido vencido, mientras que ttl establece la vigencia de las entradas. Configuración de Distribution.
Esta configuración utiliza imágenes públicas. Agregar credenciales de Docker Hub podría hacer accesibles mediante el mirror los repositorios privados permitidos para esa cuenta. No las incorpores sin proteger antes el servicio con autenticación y control de acceso.
Paso 4: crear y validar el servicio
Crea /srv/registry-mirror/compose.yaml:
services:
registry:
image: registry:3
container_name: registry-mirror
restart: unless-stopped
ports:
- "443:5000"
environment:
OTEL_TRACES_EXPORTER: none
volumes:
- ./config.yml:/etc/distribution/config.yml:ro
- ./certs:/certs:ro
- ./data:/var/lib/registry
security_opt:
- no-new-privileges:trueEn producción, sustituye registry:3 por un digest aprobado para evitar cambios inesperados. Valida el archivo antes de iniciar el contenedor:
sudo docker compose configSi la validación informa un error, corrige el YAML antes de continuar.
Paso 5: iniciar y comprobar el mirror
sudo docker compose up -d
sudo docker compose ps
sudo docker compose logs --tail=100 registryComprueba la API del registry desde un cliente autorizado:
curl -fsS https://mirror.ejemplo.com/v2/Una respuesta vacía {} con estado HTTP 200 confirma que el endpoint responde. Esto todavía no demuestra que la caché esté funcionando.
Paso 6: configurar cada cliente Docker Engine
Primero conserva una copia del archivo actual:
sudo install -d -m 0755 /etc/docker
if sudo test -f /etc/docker/daemon.json; then
sudo cp -a /etc/docker/daemon.json \
"/etc/docker/daemon.json.bak.$(date +%Y%m%d%H%M%S)"
fiEdita /etc/docker/daemon.json y agrega registry-mirrors. Si el archivo ya contiene otras opciones, intégralas en el mismo objeto JSON en lugar de sobrescribirlas:
{
"registry-mirrors": [
"https://mirror.ejemplo.com"
]
}Valida la configuración antes de reiniciar Docker:
sudo dockerd --validate --config-file=/etc/docker/daemon.jsonContinúa únicamente si aparece configuration OK. La opción --validate devuelve un código distinto de cero cuando encuentra directivas inválidas. Referencia de dockerd.
Paso 7: aplicar y verificar
El reinicio de Docker puede afectar temporalmente a los contenedores del host. Realízalo durante una ventana controlada:
sudo systemctl restart docker
sudo systemctl is-active docker
docker infoEn la salida de docker info, comprueba que aparezca:
Registry Mirrors:
https://mirror.ejemplo.com/Revisa también los mensajes recientes del daemon:
sudo journalctl -u docker --since "5 minutes ago" --no-pagerPaso 8: demostrar el uso de la caché
En el cliente, descarga una imagen pequeña que no esté almacenada localmente:
docker image rm hello-world:latest 2>/dev/null || true
docker pull hello-world:latestEn el servidor mirror, revisa los registros y el espacio utilizado:
cd /srv/registry-mirror
sudo docker compose logs --since 5m registry
sudo du -sh dataEl primer pull debe generar actividad contra el origen y crear datos en el volumen. Elimina únicamente la imagen de prueba del cliente y repite la descarga:
docker image rm hello-world:latest
docker pull hello-world:latestEn el segundo intento, las capas ya almacenadas deben servirse desde la caché. El mirror todavía puede consultar Docker Hub para verificar la vigencia de una etiqueta.
Cómo revertir el cambio
Restaura el backup de daemon.json, vuelve a validarlo y reinicia Docker:
sudo cp /etc/docker/daemon.json.bak.FECHA /etc/docker/daemon.json
sudo dockerd --validate --config-file=/etc/docker/daemon.json
sudo systemctl restart dockerPara retirar el servidor mirror sin borrar la caché:
cd /srv/registry-mirror
sudo docker compose downNo agregues --volumes ni elimines data si necesitas conservar las capas almacenadas.
Problemas frecuentes
El cliente informa x509: certificate signed by unknown authority
Comprueba el nombre incluido en el certificado, la cadena completa, su vigencia y la confianza de la CA en el cliente. No soluciones el problema agregando el servicio a insecure-registries en producción.
El mirror no aparece en docker info
Revisa la sintaxis de daemon.json, confirma que modificaste el host correcto y comprueba que Docker se reinició. Busca también opciones de inicio de dockerd que entren en conflicto con el archivo.
La carpeta de datos no crece
El cliente puede estar utilizando su caché local. Elimina solo la imagen de prueba y repite el pull. Confirma además que la imagen provenga de Docker Hub y que el cliente pueda alcanzar el mirror.
Buildx no utiliza el mirror
Los builders con driver docker-container o Kubernetes ejecutan una instancia independiente de BuildKit y pueden necesitar su propia configuración buildkitd.toml. Configuración de BuildKit.
Aparece un error HTTP 429
Los mirrors continúan sujetos a la política de uso y a los límites de descarga de Docker Hub. Una caché reduce la transferencia repetida, pero no elimina necesariamente las comprobaciones remotas. Límites de Docker Hub.