--- title: "Cómo crear un registro Docker privado en Ubuntu 24.04" description: "Monta un registro Docker privado en Ubuntu 24.04 con registry:3, autenticación htpasswd, Nginx y HTTPS de Let's Encrypt, y aprende a borrar imágenes." category: "Docker Avanzado" language: es source: https://cubepath.com/es/docs/docker-avanzado/configuracion-privada-del-registro-de-docker updated: 2026-09-25 --- # Cómo crear un registro Docker privado en Ubuntu 24.04 Un registro Docker privado te permite guardar y distribuir tus propias imágenes de contenedor sin publicarlas en Docker Hub ni depender de sus límites de descarga. La imagen oficial `registry` (el proyecto CNCF Distribution) implementa la misma API que usan `docker push` y `docker pull`. En este tutorial desplegarás el registro con Docker Compose en Ubuntu 24.04, lo protegerás con usuario y contraseña, lo publicarás detrás de Nginx con un certificado de Let's Encrypt y aprenderás a liberar espacio borrando imágenes antiguas. ## Requisitos previos Para seguir esta guía necesitas: - Un servidor con Ubuntu 24.04 LTS, por ejemplo un VPS de CubePath, con espacio en disco suficiente para tus imágenes (20 GB o más). - Un usuario no root con privilegios `sudo`. - Docker Engine y el plugin de Docker Compose instalados desde el repositorio oficial de Docker. - Un dominio con un registro DNS A, por ejemplo `registry.your_domain`, apuntando a la IP pública del servidor. - Los puertos 80 y 443 accesibles desde Internet. ## Paso 1: Crear los directorios y el usuario del registro Guarda todo lo relacionado con el registro en `/opt/registry`: la configuración, el fichero de usuarios y las capas de las imágenes: ```bash sudo mkdir -p /opt/registry/auth /opt/registry/data ``` El registro admite autenticación básica con un fichero `htpasswd` cifrado con bcrypt. La herramienta `htpasswd` está en el paquete `apache2-utils`: ```bash sudo apt update sudo apt install apache2-utils ``` Crea el fichero con tu primer usuario. La opción `-B` usa bcrypt, que es el único formato que acepta el registro, y `-c` crea el fichero. Te pedirá la contraseña dos veces: ```bash sudo htpasswd -Bc /opt/registry/auth/htpasswd your_user ``` ```output New password: Re-type new password: Adding password for user your_user ``` Para añadir más usuarios más adelante, repite el comando sin `-c`, o sobrescribirás el fichero. ## Paso 2: Configurar y arrancar el registro Crea el fichero de configuración del registro: ```bash sudo nano /opt/registry/config.yml ``` ```yaml version: 0.1 log: level: info storage: filesystem: rootdirectory: /var/lib/registry delete: enabled: true http: addr: :5000 headers: X-Content-Type-Options: [nosniff] auth: htpasswd: realm: Registry path: /auth/htpasswd ``` `storage.delete.enabled` permite borrar imágenes por la API (lo usarás en el paso 6) y el bloque `auth` exige usuario y contraseña en todas las peticiones. Crea ahora el fichero de Compose: ```bash sudo nano /opt/registry/compose.yaml ``` ```yaml services: registry: image: registry:3 restart: unless-stopped ports: - "127.0.0.1:5000:5000" volumes: - ./config.yml:/etc/distribution/config.yml:ro - ./auth:/auth:ro - ./data:/var/lib/registry ``` El puerto se publica solo en `127.0.0.1`, de modo que el registro no es accesible desde fuera sin pasar por Nginx y su HTTPS. La versión 3 de la imagen lee la configuración de `/etc/distribution/config.yml`. Arranca el registro: ```bash cd /opt/registry docker compose up -d ``` Comprueba que responde y que exige autenticación. Sin credenciales debe devolver `401`: ```bash curl -i http://127.0.0.1:5000/v2/ ``` ```output HTTP/1.1 401 Unauthorized Content-Type: application/json Docker-Distribution-Api-Version: registry/2.0 Www-Authenticate: Basic realm="Registry" ... ``` Con tu usuario, la respuesta es `200` y un JSON vacío: ```bash curl -u your_user http://127.0.0.1:5000/v2/ ``` ```output Enter host password for user 'your_user': {} ``` ## Paso 3: Publicar el registro con Nginx y HTTPS Docker exige HTTPS para cualquier registro que no esté en `localhost`. Instala Nginx y Certbot con su plugin para Nginx: ```bash sudo apt install nginx certbot python3-certbot-nginx ``` Permite el tráfico HTTP y HTTPS en el cortafuegos: ```bash sudo ufw allow OpenSSH sudo ufw allow 'Nginx Full' sudo ufw enable ``` Crea el sitio de Nginx para el registro: ```bash sudo nano /etc/nginx/sites-available/registry ``` ```nginx server { listen 80; listen [::]:80; server_name registry.your_domain; # Sin límite de tamaño: las capas de una imagen pueden ocupar varios GB client_max_body_size 0; chunked_transfer_encoding on; location /v2/ { proxy_pass http://127.0.0.1:5000; proxy_set_header Host $http_host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; proxy_read_timeout 900; proxy_request_buffering off; } } ``` Las cabeceras `Host` y `X-Forwarded-Proto` son necesarias: el registro las usa para construir las URL de subida que devuelve al cliente, y si faltan, `docker push` intenta continuar por HTTP y falla. Activa el sitio, comprueba la sintaxis y recarga Nginx: ```bash sudo ln -s /etc/nginx/sites-available/registry /etc/nginx/sites-enabled/ sudo nginx -t sudo systemctl reload nginx ``` ```output nginx: the configuration file /etc/nginx/nginx.conf syntax is ok nginx: configuration file /etc/nginx/nginx.conf test is successful ``` Pide el certificado. Certbot añade al sitio la configuración TLS y la redirección de HTTP a HTTPS, y deja programada la renovación automática: ```bash sudo certbot --nginx -d registry.your_domain ``` Comprueba el acceso por HTTPS: ```bash curl -u your_user https://registry.your_domain/v2/ ``` ```output {} ``` ## Paso 4: Subir y descargar imágenes Desde cualquier equipo con Docker (tu portátil, un servidor de CI o el propio servidor), inicia sesión en el registro: ```bash docker login registry.your_domain ``` ```output Username: your_user Password: Login Succeeded ``` Para subir una imagen al registro, su nombre debe empezar por el dominio del registro. Descarga una imagen pequeña de prueba, etiquétala y súbela: ```bash docker pull hello-world docker tag hello-world registry.your_domain/hello-world:1.0 docker push registry.your_domain/hello-world:1.0 ``` ```output The push refers to repository [registry.your_domain/hello-world] 53d204b3dc5d: Pushed 1.0: digest: sha256:... size: 525 ``` Comprueba que el registro la tiene listando los repositorios y sus etiquetas: ```bash curl -u your_user https://registry.your_domain/v2/_catalog curl -u your_user https://registry.your_domain/v2/hello-world/tags/list ``` ```output {"repositories":["hello-world"]} {"name":"hello-world","tags":["1.0"]} ``` Para confirmar la descarga, borra la copia local y vuelve a bajarla del registro: ```bash docker image rm registry.your_domain/hello-world:1.0 docker pull registry.your_domain/hello-world:1.0 ``` > Nota: `docker login` guarda las credenciales en `~/.docker/config.json` codificadas en base64, no cifradas. En equipos compartidos o servidores de CI, configura un credential helper o usa un usuario del registro dedicado a cada sistema. ## Paso 5: Hacer copia de seguridad del registro Todo el contenido del registro está en `/opt/registry/data`, y los usuarios en `/opt/registry/auth`. Para una copia consistente, para el registro un momento mientras copias los datos: ```bash cd /opt/registry docker compose stop registry sudo tar -czf /root/registry-backup-$(date +%F).tar.gz -C /opt registry docker compose start registry ``` Guarda el archivo fuera del servidor. Para restaurar, basta con extraerlo en `/opt` y ejecutar `docker compose up -d`. ## Paso 6: Borrar imágenes y liberar espacio Borrar una etiqueta no libera disco por sí solo. El proceso tiene dos partes: borrar el manifiesto por la API y después ejecutar el recolector de basura, que elimina las capas que ya no usa ninguna imagen. Obtén primero el digest del manifiesto de la etiqueta que quieres borrar. Las cabeceras `Accept` son necesarias para que el registro devuelva el digest correcto tanto de imágenes simples como multiarquitectura: ```bash curl -sI -u your_user \ -H 'Accept: application/vnd.oci.image.index.v1+json' \ -H 'Accept: application/vnd.oci.image.manifest.v1+json' \ -H 'Accept: application/vnd.docker.distribution.manifest.list.v2+json' \ -H 'Accept: application/vnd.docker.distribution.manifest.v2+json' \ https://registry.your_domain/v2/hello-world/manifests/1.0 | grep -i docker-content-digest ``` ```output Docker-Content-Digest: sha256:your_digest ``` Borra el manifiesto con ese digest: ```bash curl -u your_user -X DELETE https://registry.your_domain/v2/hello-world/manifests/sha256:your_digest ``` La respuesta es un `202 Accepted` sin cuerpo. Ahora ejecuta el recolector de basura. Debe correr con el registro parado para no borrar capas de una subida en curso: ```bash cd /opt/registry docker compose stop registry docker compose run --rm --entrypoint registry registry garbage-collect --delete-untagged /etc/distribution/config.yml docker compose start registry ``` La salida enumera los blobs marcados y eliminados. Comprueba el espacio que ocupa el registro: ```bash sudo du -sh /opt/registry/data ``` ## Solución de problemas - **`413 Request Entity Too Large` al hacer push**: Nginx está limitando el tamaño de la petición. Confirma que `client_max_body_size 0;` está dentro del bloque `server` que atiende HTTPS (Certbot modifica ese bloque, no crea uno nuevo) y recarga Nginx. - **`unauthorized: authentication required`**: el usuario o la contraseña no coinciden, o el fichero `htpasswd` no se creó con `-B`. Vuelve a crear el usuario con `sudo htpasswd -B /opt/registry/auth/htpasswd your_user` y reinicia el registro con `docker compose restart`. - **`http: server gave HTTP response to HTTPS client`**: estás usando `your_server_ip:5000` en lugar del dominio. Usa siempre `registry.your_domain`, que pasa por Nginx y HTTPS. - **`blob upload unknown` o push que falla a mitad**: falta `X-Forwarded-Proto` o `Host` en la configuración de Nginx. Revisa el bloque `location /v2/` del paso 3. ## Conclusión Tienes un registro Docker privado con autenticación, servido por HTTPS con un certificado de Let's Encrypt y con un procedimiento para liberar espacio. Como siguientes pasos, crea un usuario distinto para cada sistema de CI, programa la copia de seguridad y la recolección de basura en una ventana de mantenimiento semanal, y si el disco se queda corto, mueve el almacenamiento a un bucket compatible con S3 usando el driver `s3` del registro.