Cerraste el módulo 1 con las herramientas en la mano y el problema medido: quince pasos manuales para poner en marcha Aurora Libros. El módulo 2 empieza a demolerlos, y lo hace desde el principio de la cadena: de dónde salen las imágenes. Hasta ahora has escrito docker pull node:22-alpine o docker run nginx:alpine sin preguntarte demasiado quién está al otro lado, quién publicó esa imagen ni por qué deberías fiarte de ella. En esta lección abres esa caja. Vas a entender qué es exactamente un registro y qué es un repositorio dentro de él, te vas a autenticar con docker login y verás dónde quedan tus credenciales (con una sorpresa desagradable incluida), aprenderás a distinguir una imagen oficial de una imagen que alguien subió un martes de 2019 y nunca volvió a tocar, conocerás los límites de descarga que te pueden cortar una build en el peor momento, y montarás tu propio registro en un contenedor para ver que el mecanismo no tiene ninguna magia. Al final elegirás el repositorio donde vivirá la imagen de aurora-api durante el resto del curso.

Contenido

  1. Registro, repositorio y etiqueta: las tres capas del almacén
  2. Docker Hub: cuenta e interfaz
  3. docker login, docker logout y dónde quedan tus credenciales
  4. Tipos de imagen en Docker Hub y cómo evaluar su confianza
  5. Límites de descarga: anónimo frente a autenticado
  6. Buscar imágenes desde la terminal con docker search
  7. Repositorios públicos y privados
  8. Registros alternativos a Docker Hub
  9. Tu propio registro en un contenedor
  10. El repositorio de Aurora Libros

  1. Registro, repositorio y etiqueta: las tres capas del almacén

En la lección 01-05 aprendiste a leer un nombre completo de imagen:

registro/usuario/repositorio:etiqueta@sha256:digest

Ahora toca entender qué hay detrás de cada tramo, porque son tres cosas distintas que la gente confunde constantemente al hablar.

  • Un registro (registry) es un servicio: un servidor con una API HTTP que almacena y sirve imágenes. Docker Hub es un registro. ghcr.io es otro. El que montarás en el apartado 9 en tu portátil también lo es. Un registro contiene muchos repositorios.
  • Un repositorio (repository) es una colección de imágenes relacionadas entre sí, que comparten nombre y se distinguen por la etiqueta. library/nginx es un repositorio: dentro viven nginx:1.27, nginx:alpine, nginx:latest… todas son "nginx", en distintas versiones o variantes.
  • Una etiqueta (tag) es una referencia legible que apunta a un manifiesto concreto, identificado a su vez por un digest sha256:… inmutable.
flowchart TB
    subgraph REG["Registro · docker.io (Docker Hub)"]
        subgraph R1["Repositorio: library/node"]
            T1["etiqueta 22-alpine"]
            T2["etiqueta 22"]
            T3["etiqueta latest"]
        end
        subgraph R2["Repositorio: auroralibros/aurora-api"]
            T4["etiqueta 1.0.0"]
            T5["etiqueta latest"]
        end
    end

    D1["sha256:9f2c…<br/>manifiesto + capas"]
    D2["sha256:41ab…<br/>manifiesto + capas"]

    T1 --> D1
    T2 --> D2
    T3 --> D2
    T4 --> D1
    T5 --> D1

Fíjate en dos detalles del diagrama que explican comportamientos que ya has visto:

  • Varias etiquetas pueden apuntar al mismo digest. 22 y latest son, casi siempre, la misma imagen con dos nombres. Por eso en la lección 01-05 docker image ls te mostraba dos filas con el mismo IMAGE ID: no eran dos imágenes, era una con dos referencias. Volverás a esto con detalle en la lección 02-06.
  • El digest es lo único inmutable. La etiqueta 22-alpine apunta hoy a un manifiesto y dentro de tres semanas puede apuntar a otro (parche de seguridad de Node, actualización de Alpine). El digest sha256:9f2c… siempre es exactamente los mismos bytes.

Una analogía útil: el registro es la biblioteca; el repositorio es la estantería dedicada a una obra; las etiquetas son las pegatinas ("primera edición", "última edición", "edición de bolsillo") pegadas a ejemplares concretos. Mover una pegatina no cambia el ejemplar; solo cambia a qué ejemplar apunta ese nombre.

El nombre completo y sus abreviaturas

Cuando escribes docker pull nginx:alpine, Docker expande el nombre así:

Lo que escribes Lo que Docker entiende realmente
nginx docker.io/library/nginx:latest
nginx:alpine docker.io/library/nginx:alpine
auroralibros/aurora-api:1.0.0 docker.io/auroralibros/aurora-api:1.0.0
ghcr.io/auroralibros/aurora-api:1.0.0 tal cual: registro explícito
localhost:5000/aurora-api:1.0.0 tal cual: registro local en el puerto 5000

Tres reglas que se deducen de la tabla:

  1. Si no pones registro, se asume Docker Hub (docker.io). Es una decisión histórica del cliente de Docker, no un estándar; otras herramientas (Podman, por ejemplo, lección 07-05) pueden preguntarte qué registro quieres.
  2. Si no pones usuario/organización, se asume library/, el espacio de nombres reservado a las imágenes oficiales. Por eso nginx y library/nginx son lo mismo, y por eso nadie más puede publicar un repositorio llamado simplemente nginx.
  3. Si no pones etiqueta, se asume latest, con todos los peligros que ya conoces.

Puedes comprobar la primera y la segunda con un comando que ya manejas:

docker pull nginx:alpine
docker image ls nginx --format "table {{.Repository}}\t{{.Tag}}\t{{.Size}}"
REPOSITORY   TAG       SIZE
nginx        alpine    52.5MB

Docker te muestra la forma corta porque el registro es el predeterminado. Si haces lo mismo con una imagen de otro registro, el nombre aparecerá completo, con ghcr.io/… delante. Esa asimetría en la salida es una pista rápida para saber de dónde viene cada imagen de tu máquina.

  1. Docker Hub: cuenta e interfaz

Docker Hub (hub.docker.com) es el registro público por defecto de Docker: aloja millones de repositorios y es donde han ido a buscar todas las imágenes que has descargado hasta ahora. Vas a necesitar una cuenta para dos cosas: subir la imagen de aurora-api (lección 02-06) y —más inmediato— duplicar tu cuota de descargas.

Crear la cuenta

  1. Entra en https://hub.docker.com y pulsa Sign Up.
  2. Elige un Docker ID. Piénsalo bien: será el espacio de nombres de todas tus imágenes públicas y no se puede cambiar después. En el curso usaremos auroralibros como Docker ID ficticio del alumno; tú usarás el tuyo real allí donde aparezca.
  3. Verifica el correo electrónico.
  4. Activa la verificación en dos pasos (Account Settings → Security). No es opcional en la práctica: tu cuenta puede acabar publicando imágenes que otros ejecutan, y una cuenta comprometida es un vector de ataque de manual.

Recorrido por la interfaz

Una vez dentro, estas son las zonas que usarás:

Zona Para qué sirve
Repositories Tus repositorios: crear, ver, cambiar visibilidad, borrar
Explore Buscador de imágenes públicas, con filtros por tipo (Official, Verified Publisher…)
Organizations Espacios compartidos por un equipo, con permisos por miembro
Account Settings → Security Contraseña, 2FA y Personal Access Tokens
Usage Consumo de descargas y almacenamiento de tu cuenta

La página de un repositorio

Abre https://hub.docker.com/_/node (la barra baja indica imagen oficial) y observa la estructura, porque es idéntica en todos los repositorios:

  • Descripción: qué es la imagen, cómo se usa, variables de entorno que acepta, ejemplos. En las imágenes bien mantenidas, esta página es la documentación real del producto.
  • Comando de descarga arriba a la derecha: el docker pull node listo para copiar.
  • Pestaña Tags: el catálogo de etiquetas disponibles. Es la pestaña que más vas a usar. Para cada etiqueta te muestra el tamaño comprimido, las arquitecturas disponibles (linux/amd64, linux/arm64…) y cuándo se actualizó por última vez. Buscar ahí "22-alpine" antes de escribirlo en un FROM te ahorra el clásico manifest unknown por una etiqueta que no existe.
  • Enlace al Dockerfile: en las imágenes serias, cada etiqueta enlaza al repositorio de GitHub donde está el Dockerfile que la construyó. Poder leer ese fichero es una de las mejores señales de confianza que existen.

Un ejercicio de dos minutos que conviene hacer ahora: entra en la pestaña Tags de node, busca 22-alpine y anota su tamaño comprimido y su fecha de actualización. Compara con 22 a secas. La diferencia de tamaño explica por qué en la lección 01-07 elegiste Alpine para aurora-api.

  1. docker login, docker logout y dónde quedan tus credenciales

Para subir imágenes o para descargar de repositorios privados necesitas autenticarte. El comando es directo:

docker login
Log in with your Docker ID or email address to push and pull images from Docker Hub.
Username: auroralibros
Password:
Login Succeeded

Sin argumentos, docker login apunta a Docker Hub. Para otro registro, se le pasa como argumento:

docker login ghcr.io
docker login registry.gitlab.com
docker login localhost:5000

Y para cerrar sesión:

docker logout
docker logout ghcr.io

Dónde quedan las credenciales (y por qué debe preocuparte)

Tras un login correcto, Docker guarda la credencial en ~/.docker/config.json. Míralo:

cat ~/.docker/config.json
{
  "auths": {
    "https://index.docker.io/v1/": {
      "auth": "YXVyb3JhbGlicm9zOmRja3JfcGF0X2VqZW1wbG8="
    }
  }
}

Ese campo auth no está cifrado. Es simplemente usuario:contraseña codificado en base64, que es una codificación reversible, no un sistema de protección. Compruébalo tú mismo:

echo 'YXVyb3JhbGlicm9zOmRja3JfcGF0X2VqZW1wbG8=' | base64 -d
auroralibros:dckr_pat_ejemplo

Cualquiera con acceso de lectura a tu ~/.docker/config.json —otro usuario del sistema con permisos, un proceso malicioso, una copia de seguridad mal protegida o un contenedor al que le montaste el directorio por descuido— tiene tus credenciales en claro. De hecho, Docker te avisa de ello al iniciar sesión:

WARNING! Your password will be stored unencrypted in /home/joan/.docker/config.json.
Configure a credential helper to remove this warning. See
https://docs.docker.com/go/credential-store/

Tres medidas concretas para mitigarlo, de menor a mayor esfuerzo:

a) Usa un token de acceso personal en lugar de tu contraseña. En Docker Hub, Account Settings → Personal Access Tokens → Generate new token, con permisos mínimos (Public Repo Read-only si solo descargas, Read & Write si vas a publicar). Ventajas: si se filtra, se revoca ese token sin cambiar tu contraseña, no da acceso a la web ni a la configuración de la cuenta, y puedes tener uno por máquina. Se usa igual:

docker login -u auroralibros
# En "Password:" pegas el token, no tu contraseña

b) Evita docker login -p en scripts. Escribir la contraseña en la línea de comandos la deja en el historial del shell y en la lista de procesos. La forma correcta es por entrada estándar:

echo "$DOCKER_TOKEN" | docker login -u auroralibros --password-stdin

--password-stdin lee la credencial de la tubería. Es el patrón obligatorio en CI/CD (lo verás aplicado en la lección 06-02).

c) Configura un credential helper. Es un programa externo que guarda las credenciales en el almacén seguro del sistema operativo en lugar de en un fichero de texto:

Sistema Helper Almacén real
Linux (escritorio) docker-credential-secretservice Keyring de GNOME / KWallet vía D-Bus
Linux (servidor, sin GUI) docker-credential-pass pass, respaldado por GPG
macOS docker-credential-osxkeychain Llavero de macOS (viene con Docker Desktop)
Windows docker-credential-wincred Administrador de credenciales de Windows

Instalación y activación en Linux de escritorio:

sudo apt install -y golang-docker-credential-helpers

Y edita ~/.docker/config.json para añadir la línea del helper:

{
  "auths": {},
  "credsStore": "secretservice"
}

Ahora vuelve a autenticarte:

docker logout
docker login -u auroralibros
cat ~/.docker/config.json
{
  "auths": {
    "https://index.docker.io/v1/": {}
  },
  "credsStore": "secretservice"
}

El objeto de auths está vacío: la credencial ya no está en el fichero, sino en el llavero del sistema, y el aviso ha desaparecido. Docker la recupera del helper cada vez que la necesita.

  1. Tipos de imagen en Docker Hub y cómo evaluar su confianza

Descargar una imagen y ejecutarla es, en el fondo, ejecutar software de un desconocido en tu máquina. Docker Hub etiqueta los repositorios en categorías que te ayudan a decidir de quién te fías.

Tipo Insignia en la web Quién la mantiene Espacio de nombres Cuándo usarla
Docker Official Image Docker Official Image Equipo de Docker + mantenedores designados, con Dockerfiles públicos y revisados library/ (sin usuario: nginx, node, postgres) Primera opción siempre para software base: lenguajes, bases de datos, servidores web
Verified Publisher Verified Publisher La empresa propietaria del software, con identidad verificada por Docker El de la empresa (grafana/grafana, bitnami/…) Cuando necesitas el producto comercial de ese fabricante
Sponsored OSS Sponsored OSS Proyecto de código abierto reconocido, patrocinado por Docker El del proyecto Proyectos abiertos consolidados sin imagen oficial
Comunidad ninguna Cualquier persona con una cuenta Cualquiera Solo tras auditarla, o nunca en producción

Para Aurora Libros esto se traduce en algo muy concreto: node, postgres, redis y nginx son oficiales, y por eso son las cuatro bases del proyecto. No hay ninguna razón para usar algunusuario/node-22-con-todo en su lugar.

Criterios prácticos de evaluación

Las insignias no cubren todos los casos: tarde o temprano necesitarás una imagen de la comunidad. Este es el chequeo que conviene aplicar antes de escribirla en un FROM:

  1. Fecha de la última actualización. Está en la pestaña Tags. Una imagen que no se toca desde hace un año arrastra un año de vulnerabilidades sin parchear del sistema base. Es el criterio que más descarta.
  2. ¿El Dockerfile es público? Si no puedes leer cómo se construyó, estás ejecutando una caja negra. Busca el enlace a GitHub en la descripción.
  3. Tamaño. Una imagen de 2 GB para servir un binario de 10 MB indica que lleva dentro medio sistema operativo con herramientas que no necesitas y que amplían la superficie de ataque.
  4. Número y contenido de las capas. Con lo aprendido en 01-05 puedes auditar esto sin descargar nada más que la imagen:
docker pull redis:7-alpine
docker image history redis:7-alpine --no-trunc --format "table {{.Size}}\t{{.CreatedBy}}" | head -12

Lo que buscas en esa salida: instrucciones comprensibles y justificables. Una capa con un curl http://…/instalador.sh | sh apuntando a un dominio desconocido es una bandera roja inmediata; significa que la imagen descarga y ejecuta código arbitrario de un tercero en tiempo de construcción.

  1. Descargas y estrellas, con reservas. Un millón de descargas indica que mucha gente la usa, no que sea segura; los números se inflan fácilmente con automatizaciones. Úsalo como señal débil, nunca como argumento principal.
  2. ¿Publica varias arquitecturas? Si trabajas en un Mac con Apple Silicon o en un servidor ARM, necesitas linux/arm64. La pestaña Tags lo indica. Si solo hay amd64, funcionará por emulación, más lenta (los manifiestos multi-arquitectura se ven en la lección 05-05).

Todo esto es evaluación manual y previa. El análisis automático del contenido de una imagen en busca de vulnerabilidades conocidas (docker scout, escáneres en el pipeline) y la firma criptográfica de imágenes son otra disciplina, y se tratan en la lección 05-03.

  1. Límites de descarga: anónimo frente a autenticado

Docker Hub limita cuántas imágenes puedes descargar por unidad de tiempo. Es un límite real que corta builds en producción, y conviene conocerlo antes de que te pase.

Situación Límite orientativo Cómo se identifica
Anónimo (sin docker login) El más bajo de todos, contado por dirección IP Por IP pública de salida
Cuenta gratuita autenticada Sensiblemente superior al anónimo Por cuenta de usuario
Cuenta de pago (Pro / Team / Business) Muy alto o sin límite práctico Por cuenta

Las cifras exactas las revisa Docker periódicamente, así que consulta siempre la página oficial de precios antes de dimensionar nada. Lo que no cambia es la estructura del problema, y hay un matiz crítico: en el caso anónimo el contador es por IP. Si estás en una oficina, en una universidad o detrás del NAT de un proveedor de nube, compartes cuota con todos los demás. Una empresa entera detrás de una sola IP pública puede agotar el límite en minutos sin que nadie haya hecho nada raro.

Cuando lo superas, el error es este:

Error response from daemon: toomanyrequests: You have reached your pull rate limit.
You may increase the limit by authenticating and upgrading:
https://www.docker.com/increase-rate-limit

Y si ocurre a mitad de una construcción, la verás así:

ERROR: failed to solve: node:22-alpine: failed to resolve source metadata for
docker.io/library/node:22-alpine: toomanyrequests: You have reached your pull rate limit.

Cómo evitarlo, por orden de eficacia:

  • Autentícate siempre, incluso para descargar imágenes públicas. Un simple docker login en tu máquina y en los agentes de CI duplica la cuota y la aísla por cuenta en lugar de por IP.
  • Aprovecha la caché local. Las capas ya descargadas no vuelven a bajarse. Un agente de CI efímero que empieza siempre en blanco descarga todo cada vez: es el escenario que más consume.
  • Usa un registro espejo o un caché de tirones (pull-through cache) en tu organización. El registro del apartado 9 puede configurarse justamente para eso.
  • Consume las imágenes base desde otro registro. Muchas imágenes están espejadas en ghcr.io, en ECR Public o en mcr.microsoft.com, con políticas de límite distintas.

Puedes comprobar tu situación consultando la cabecera que devuelve el registro al pedir un manifiesto, sin descargar la imagen:

TOKEN=$(curl -s "https://auth.docker.io/token?service=registry.docker.io&scope=repository:ratelimitpreview/test:pull" | grep -o '"token":"[^"]*' | cut -d'"' -f4)
curl -s --head -H "Authorization: Bearer $TOKEN" https://registry-1.docker.io/v2/ratelimitpreview/test/manifests/latest | grep -i ratelimit
ratelimit-limit: 100;w=21600
ratelimit-remaining: 76;w=21600

La lectura es: cuota de 100 descargas en una ventana de 21 600 segundos (6 horas), de las que te quedan 76. Es una comprobación útil cuando un pipeline empieza a fallar de forma intermitente sin cambios en el código.

  1. Buscar imágenes desde la terminal con docker search

No hace falta abrir el navegador para buscar en Docker Hub:

docker search postgres --limit 5
NAME                DESCRIPTION                                     STARS   OFFICIAL
postgres            The PostgreSQL object-relational database ...   14231   [OK]
bitnami/postgresql  Bitnami container image for PostgreSQL          202
timescale/timescaledb  TimescaleDB - time-series database ...        130
postgrest/postgrest  REST API for any PostgreSQL database           102
paradedb/paradedb   Postgres for Search and Analytics                 8

Columnas: nombre del repositorio, descripción, estrellas y si es oficial. La primera fila no lleva usuario delante, la marca inequívoca de que vive en library/.

Filtros y formato disponibles:

# Solo imágenes oficiales
docker search --filter is-official=true node

# Solo repositorios con al menos 100 estrellas
docker search --filter stars=100 redis

# Con formato personalizado, reutilizando lo aprendido en 01-04
docker search nginx --limit 5 --format "table {{.Name}}\t{{.StarCount}}\t{{.IsOfficial}}"
NAME                STARS     OFFICIAL
nginx               21045     [OK]
nginxinc/nginx-unprivileged  145
nginxproxy/nginx-proxy       130
bitnami/nginx                191
nginx/nginx-prometheus-exporter  58

Dos limitaciones importantes de docker search que hay que tener claras:

  1. Solo busca en Docker Hub. No consulta ghcr.io ni ningún otro registro, aunque tengas sesión abierta en ellos. Es una limitación del propio comando, no de tu configuración.
  2. No lista etiquetas. Te dice que existe el repositorio node, pero no qué versiones hay dentro. Para eso, la pestaña Tags de la web o una consulta a la API del registro:
curl -s "https://hub.docker.com/v2/repositories/library/node/tags?page_size=100&name=22-alpine" \
  | grep -o '"name":"[^"]*' | cut -d'"' -f4
22-alpine
22-alpine3.21
22-alpine3.20

Este comando consulta la API pública de Docker Hub y extrae los nombres de las etiquetas que contienen 22-alpine. Es la forma rápida de verificar desde la terminal que una etiqueta existe antes de ponerla en un FROM y descubrir el error a mitad de la build.

  1. Repositorios públicos y privados

Al crear un repositorio en Docker Hub eliges su visibilidad, y la decisión tiene consecuencias prácticas:

Aspecto Público Privado
Quién puede descargar Cualquiera, sin autenticarse Solo cuentas con permiso, siempre autenticadas
Quién puede subir Solo tú o tu organización Solo tú o tu organización
Aparece en búsquedas No
Coste Gratuito e ilimitado Cupo limitado en el plan gratuito; de pago a partir de ahí
Consume cuota de descargas del que la baja
Uso típico Software libre, imágenes de ejemplo, formación Aplicaciones de empresa, cualquier cosa con lógica de negocio

La regla de oro: una imagen pública es código publicado. Todo lo que hay dentro —tu server.js, tus rutas internas, los comentarios del código, cualquier fichero que se coló en el COPY— puede ser inspeccionado y extraído por cualquiera. Esto conecta directamente con la advertencia del ejercicio 3 de la lección 01-07: las credenciales nunca entran en la imagen, y con un repositorio público esa regla pasa de ser una buena práctica a ser crítica.

Para el curso, auroralibros/aurora-api será público: así podrás compartir el enlace y ejecutarlo desde cualquier máquina sin autenticarte, que es exactamente lo que quieres demostrar. En la lección 02-06 verás cómo cambiar la visibilidad a privada y cómo dar acceso a un equipo, que es lo que haría Aurora Libros de verdad.

  1. Registros alternativos a Docker Hub

Docker Hub no es obligatorio. Estos son los registros que te vas a encontrar en el mundo real:

Registro Prefijo Punto fuerte Autenticación
Docker Hub docker.io/ (implícito) El catálogo público más grande; imágenes oficiales Docker ID + token
GitHub Container Registry ghcr.io/ Integrado con el repositorio de código y con GitHub Actions; privados gratuitos ilimitados Usuario de GitHub + personal access token con permiso write:packages
Amazon ECR <id>.dkr.ecr.<región>.amazonaws.com/ Permisos con IAM, cercanía a las cargas de AWS, escaneo integrado Token temporal vía aws ecr get-login-password
GitLab Container Registry registry.gitlab.com/ Integrado con GitLab CI, un registro por proyecto Usuario + deploy token o token de CI
Harbor tu dominio Registro autoalojado de la CNCF: replicación, políticas de retención, control de acceso fino LDAP/OIDC o usuarios locales
Registry (registry:2) tu dominio o localhost:5000 La implementación de referencia, mínima y sin interfaz web Opcional (básica o token)

La buena noticia es que todos hablan el mismo protocolo, la OCI Distribution Specification. Por eso los comandos son idénticos en todos ellos: cambia el prefijo del nombre y nada más. Un docker push a Docker Hub y uno a Harbor son la misma operación contra servidores distintos. Lo comprobarás ahora mismo.

  1. Tu propio registro en un contenedor

Nada aclara mejor qué es un registro que levantar uno. La imagen oficial registry:2 implementa el protocolo de distribución y cabe en unos pocos megabytes.

docker run -d \
  --name aurora-registry \
  -p 5000:5000 \
  registry:2
Unable to find image 'registry:2' locally
2: Pulling from library/registry
...
Status: Downloaded newer image for registry:2
7c9a1e4f0b2d8a3c6f1b5e2d4a8c7b9e3f1a6d2c8b4e7a9f1c3d5b8e2a4f6c9d

Repasa las opciones, todas conocidas de la lección 01-06: -d lo deja en segundo plano, --name le da un nombre manejable y -p 5000:5000 publica el puerto del registro en tu máquina. Comprueba que responde:

curl http://localhost:5000/v2/_catalog
{"repositories":[]}

/v2/ es la raíz de la API de distribución OCI, y _catalog lista los repositorios. Está vacío: acabas de estrenarlo. Vamos a meter algo dentro.

# 1. Traer una imagen pequeña desde Docker Hub
docker pull alpine:3.21

# 2. Crear una referencia nueva que apunte a tu registro
docker tag alpine:3.21 localhost:5000/alpine:3.21

# 3. Subirla
docker push localhost:5000/alpine:3.21
The push refers to repository [localhost:5000/alpine]
6e771e15690e: Pushed
3.21: digest: sha256:21dc6063fd678b478f57c0e13f47560d0ea4eeba26dfc947b2a4f81f686b9f45 size: 528

Qué acaba de pasar, paso a paso:

  • docker tag no copia ni duplica nada: crea un nombre nuevo para la misma imagen local. alpine:3.21 y localhost:5000/alpine:3.21 comparten IMAGE ID. Es el mecanismo que estudiarás a fondo en 02-06.
  • El prefijo localhost:5000/ es lo que determina el destino del push. Docker no tiene un comando "sube a este servidor": el servidor forma parte del nombre de la imagen. Esta es la idea clave de toda la lección.
  • La salida muestra la capa subida y el digest resultante, el mismo identificador inmutable del apartado 1.

Verifica el catálogo y las etiquetas del repositorio:

curl -s http://localhost:5000/v2/_catalog
curl -s http://localhost:5000/v2/alpine/tags/list
{"repositories":["alpine"]}
{"name":"alpine","tags":["3.21"]}

Y ahora la prueba de fuego: borra la imagen local y recupérala desde tu registro.

docker image rm localhost:5000/alpine:3.21 alpine:3.21
docker pull localhost:5000/alpine:3.21
docker run --rm localhost:5000/alpine:3.21 cat /etc/alpine-release
3.21.0

Has completado un ciclo entero —pull de Docker Hub, tag, push a tu registro, borrado local, pull de tu registro, run— con los mismos comandos que usarías contra cualquier registro del mundo. La única diferencia entre tu registro de juguete y el de una multinacional es el prefijo del nombre y quién administra el servidor.

Un detalle importante que descubrirás en cuanto pases del localhost: Docker exige HTTPS para cualquier registro remoto. localhost está exento por ser local. Si intentas usar 192.168.1.50:5000 sin TLS, obtendrás:

Error response from daemon: Get "https://192.168.1.50:5000/v2/": http: server gave HTTP response to HTTPS client

La solución correcta es poner un certificado válido delante del registro. La solución rápida para una red de laboratorio es declararlo como registro inseguro en /etc/docker/daemon.json y reiniciar el demonio:

{
  "insecure-registries": ["192.168.1.50:5000"]
}
sudo systemctl restart docker

Úsalo solo en entornos aislados y de prueba: sin TLS, cualquiera en la red puede leer y alterar las imágenes en tránsito.

Cuando termines de experimentar, limpia:

docker stop aurora-registry && docker rm aurora-registry

Ten en cuenta que las imágenes que subiste vivían dentro del contenedor y desaparecen con él, porque no montaste ningún volumen. Un registro real necesita almacenamiento persistente, y ese es justamente el tema de la lección 03-06.

  1. El repositorio de Aurora Libros

Con todo lo anterior, ya puedes tomar la decisión que gobierna el resto del módulo. La imagen de la API de Aurora Libros vivirá en:

docker.io/auroralibros/aurora-api

Que escribirás en su forma corta:

auroralibros/aurora-api

Justificación de cada tramo:

Tramo Valor Por qué
Registro docker.io (implícito) Es el predeterminado, no requiere configuración y es el que más gente puede consumir sin fricción
Usuario auroralibros El Docker ID de la organización. Sustitúyelo por el tuyo real
Repositorio aurora-api Un repositorio por servicio, no uno por proyecto: aurora-api y, si algún día hiciera falta, aurora-web
Visibilidad Pública Para poder ejecutarla desde cualquier máquina sin autenticarse, que es la demostración del módulo

La regla de "un repositorio por servicio" merece un comentario, porque es un error frecuente hacerlo al revés: meter varias imágenes distintas en un mismo repositorio distinguiéndolas por la etiqueta (aurora:api-1.0.0, aurora:web-1.0.0). Funciona técnicamente, pero rompe todo lo demás: no puedes dar permisos distintos por servicio, las etiquetas dejan de significar "versión" y la pestaña Tags se vuelve ilegible. Un repositorio por artefacto desplegable.

Como preparación para la lección 02-06, crea ya el repositorio vacío desde la web (Repositories → Create repository), con nombre aurora-api, visibilidad pública y una descripción corta como "API REST del catálogo de Aurora Libros S.L.". No es imprescindible —Docker Hub crea el repositorio automáticamente en el primer push—, pero así fijas la descripción y la visibilidad desde el principio y evitas publicar por accidente algo privado en público.

En 02-02 construirás la imagen que llenará ese repositorio.

Errores Comunes y Consejos

  • Confundir registro con repositorio. "Sube la imagen al repositorio de Docker" no significa nada preciso. El registro es el servidor (docker.io); el repositorio es el nombre dentro de él (auroralibros/aurora-api). Cuando depures un push fallido, la primera pregunta siempre es a qué registro apunta el nombre completo de la imagen.
  • Creer que docker login "cambia de registro". No hay un registro activo. Puedes tener sesión abierta simultáneamente en Docker Hub, ghcr.io y tu registro local; el destino de cada operación lo decide el prefijo del nombre de la imagen, no un estado global.
  • Dejar la contraseña real en ~/.docker/config.json. Base64 no es cifrado. Usa un token de acceso personal con permisos mínimos y, a poder ser, un credential helper. En un servidor compartido esto no es una recomendación, es un requisito.
  • docker login -p miContraseña en un script. Queda en el historial del shell y es visible en ps para cualquier usuario de la máquina. Usa --password-stdin siempre.
  • Builds anónimas en CI. Es la causa número uno del toomanyrequests que aparece "de repente" un martes por la tarde: la IP compartida del proveedor agotó la cuota. Autentica los agentes de CI aunque solo descarguen imágenes públicas.
  • Fiarse de las estrellas. Una imagen de la comunidad con muchas descargas y sin actualizar desde hace catorce meses es peor opción que una oficial menos popular. Prioriza en este orden: oficial → verificada → comunidad con Dockerfile público y actualizada.
  • Consejo: mira siempre la pestaña Tags antes de escribir un FROM. Confirma que la etiqueta existe, con qué arquitecturas y cuándo se actualizó. Te ahorra el manifest unknown a mitad de una build.
  • Consejo: un registro local es la mejor herramienta de aprendizaje. Cuando algo del flujo tag/push/pull no te cuadre, reprodúcelo contra localhost:5000: es instantáneo, no consume cuota y no publica nada en Internet.

Ejercicios

Ejercicio 1: audita tres imágenes antes de confiar en ellas

Elige tres repositorios de Docker Hub: uno oficial (postgres), uno de Verified Publisher o Sponsored OSS a tu elección, y uno de comunidad que encuentres buscando "nodejs" en Explore. Para cada uno, rellena esta tabla y decide si lo usarías como base de un servicio de Aurora Libros:

Criterio Imagen A Imagen B Imagen C
Nombre completo
Tipo (oficial/verificada/comunidad)
Última actualización de la etiqueta que usarías
Tamaño comprimido
¿Dockerfile público? URL
Arquitecturas disponibles
¿La usarías en producción? Motivo

Apóyate en la web para los tres primeros criterios y en docker image history para inspeccionar cómo se construyeron.

Ejercicio 2: monta un registro y publica una imagen en él

  1. Levanta un registro local en el puerto 5001 (no 5000, para practicar el mapeo) llamado aurora-registry-lab.
  2. Descarga redis:7-alpine de Docker Hub.
  3. Publícala en tu registro local con el nombre localhost:5001/aurora-cache:7.
  4. Consulta el catálogo del registro y la lista de etiquetas del repositorio con curl.
  5. Borra ambas referencias locales y vuelve a descargar la imagen desde tu registro.
  6. Arranca un contenedor con ella y comprueba que Redis responde.
  7. Limpia todo.

Ejercicio 3: diagnostica cuatro problemas de registro

Explica la causa de cada mensaje y el comando exacto con el que lo resolverías:

a) Error response from daemon: pull access denied for aurora-api, repository does not
   exist or may require 'docker login': denied: requested access to the resource is denied

b) denied: requested access to the resource is denied
   (al ejecutar: docker push aurora-api:1.0.0)

c) Error response from daemon: toomanyrequests: You have reached your pull rate limit.

d) Error response from daemon: Get "https://registro.interno.local:5000/v2/":
   x509: certificate signed by unknown authority

Soluciones

Solución al ejercicio 1

Un resultado típico, con los valores del momento de escribir esta lección (los tuyos variarán, lo importante es el razonamiento):

Criterio postgres:16-alpine grafana/grafana:11.4.0 algunusuario/nodejs:latest
Tipo Docker Official Image Verified Publisher Comunidad
Última actualización Hace días Hace días Hace 2 años
Tamaño comprimido ~110 MB ~180 MB ~380 MB
Dockerfile público Sí, github.com/docker-library/postgres Sí, github.com/grafana/grafana No
Arquitecturas amd64, arm64, y varias más amd64, arm64 solo amd64
¿Producción? , es la referencia , es el fabricante No

El razonamiento para descartar la tercera es acumulativo, y cualquiera de los puntos bastaría por sí solo:

  • Dos años sin actualizar significa dos años de CVE sin parchear en el sistema base. Aunque el código de la aplicación fuera perfecto, la libc, openssl y el resto del sistema no lo son.
  • Sin Dockerfile público no puedes saber qué hay dentro ni auditarlo.
  • latest como única etiqueta es un síntoma de proyecto no mantenido: no hay versionado, así que no puedes fijar nada ni volver atrás.
  • Solo amd64 la hace inservible en un Mac con Apple Silicon o en instancias ARM salvo emulación.
  • El tamaño delata un sistema base completo con herramientas innecesarias.

La conclusión práctica para Aurora Libros: las cuatro bases del proyecto (node, postgres, redis, nginx) son oficiales, y esa es precisamente la razón por la que se eligieron en la lección 01-07.

Solución al ejercicio 2

# 1. Registro en el puerto 5001 del host
docker run -d --name aurora-registry-lab -p 5001:5000 registry:2

Fíjate en -p 5001:5000: el registro siempre escucha en el 5000 dentro del contenedor; lo que cambias es el puerto del host. Es exactamente el mapeo puerto-host → puerto-contenedor de la lección 01-06.

# 2. Descargar de Docker Hub
docker pull redis:7-alpine

# 3. Reetiquetar y publicar
docker tag redis:7-alpine localhost:5001/aurora-cache:7
docker push localhost:5001/aurora-cache:7
The push refers to repository [localhost:5001/aurora-cache]
7: digest: sha256:0d1a3e0c... size: 1571
# 4. Consultar el registro
curl -s http://localhost:5001/v2/_catalog
curl -s http://localhost:5001/v2/aurora-cache/tags/list
{"repositories":["aurora-cache"]}
{"name":"aurora-cache","tags":["7"]}
# 5. Borrar ambas referencias locales y recuperar del registro propio
docker image rm redis:7-alpine localhost:5001/aurora-cache:7
docker image ls | grep -E "redis|aurora-cache"   # no debe devolver nada
docker pull localhost:5001/aurora-cache:7

El paso 5 es el que demuestra el objetivo del ejercicio: al borrar las dos referencias, la imagen desaparece de verdad de tu máquina (recuerda de 01-05 que dos etiquetas sobre el mismo IMAGE ID cuentan como una sola imagen: mientras quede una referencia, los datos siguen ahí). Si el pull posterior funciona, los bytes vienen necesariamente de tu registro.

# 6. Arrancar y comprobar
docker run -d --name cache-lab -p 6379:6379 localhost:5001/aurora-cache:7
docker exec cache-lab redis-cli ping
PONG
# 7. Limpieza
docker stop cache-lab aurora-registry-lab
docker rm cache-lab aurora-registry-lab
docker image rm localhost:5001/aurora-cache:7

Solución al ejercicio 3

(a) pull access denied ... may require 'docker login'. El mensaje mezcla dos causas porque el registro no distingue entre "no existe" y "no tienes permiso": hacerlo permitiría averiguar qué repositorios privados tiene una organización. Las causas reales, por frecuencia:

  1. El nombre no lleva usuario: aurora-api se expande a docker.io/library/aurora-api, y library/ está reservado a imágenes oficiales, así que no existe. Correcto: docker pull auroralibros/aurora-api:1.0.0.
  2. El repositorio es privado y no tienes sesión: docker login.
  3. Simplemente hay una errata en el nombre.

(b) denied al hacer push. Estás intentando subir a docker.io/library/aurora-api, un espacio donde nadie tiene permiso de escritura. La imagen debe llevar tu espacio de nombres:

docker tag aurora-api:1.0.0 auroralibros/aurora-api:1.0.0
docker login
docker push auroralibros/aurora-api:1.0.0

Regla general: si el push da denied, mira el nombre antes que las credenciales. Es más frecuente el nombre mal formado que la sesión caducada.

(c) toomanyrequests. Has agotado la cuota de descargas. Solución inmediata:

docker login -u auroralibros

Si ya estabas autenticado, la cuota agotada es la de tu cuenta y toca esperar a que se renueve la ventana, usar un espejo o subir de plan. En CI, la causa casi siempre es que los agentes descargan de forma anónima: añade un paso de login con --password-stdin al principio del pipeline (lección 06-02). Para confirmar el diagnóstico, la consulta de cabeceras ratelimit-* del apartado 5.

(d) x509: certificate signed by unknown authority. El registro habla HTTPS, pero su certificado está firmado por una autoridad que tu máquina no reconoce (típicamente una CA interna de la empresa). No es un problema de credenciales. La solución correcta es instalar el certificado de esa CA donde Docker lo busca:

sudo mkdir -p /etc/docker/certs.d/registro.interno.local:5000
sudo cp ca-empresa.crt /etc/docker/certs.d/registro.interno.local:5000/ca.crt
sudo systemctl restart docker

El nombre del directorio debe coincidir exactamente con el host y puerto que usas en el nombre de la imagen. La alternativa de añadirlo a insecure-registries no arregla esto: desactivaría la verificación en lugar de resolverla, y solo es aceptable en un laboratorio aislado. Distínguelo del error del apartado 9 (server gave HTTP response to HTTPS client), que es el caso contrario: allí el servidor no tiene TLS en absoluto.

Conclusión

Ya sabes de dónde vienen las imágenes que ejecutas. Un registro es un servidor que habla el protocolo de distribución OCI; dentro tiene repositorios, colecciones de imágenes que comparten nombre; y dentro de cada repositorio, las etiquetas son punteros móviles a digests inmutables. Ese modelo de tres capas explica por qué nginx significa realmente docker.io/library/nginx:latest, por qué dos etiquetas pueden compartir IMAGE ID y por qué el destino de un push no es un parámetro del comando sino parte del nombre de la imagen.

Además, ya tienes cuenta en Docker Hub, sabes que docker login deja tus credenciales en ~/.docker/config.json en base64 —no cifrado— y cómo evitarlo con tokens de acceso personal, --password-stdin y un credential helper. Puedes distinguir una Docker Official Image de una imagen de comunidad abandonada y aplicar un chequeo real de confianza: fecha de actualización, tamaño, Dockerfile público, capas comprensibles y arquitecturas. Conoces los límites de descarga, por qué golpean sobre todo a las builds anónimas detrás de una IP compartida y qué error exacto producen. Y has levantado tu propio registro con registry:2 para comprobar, con un ciclo completo de tag, push, borrado y pull, que el mecanismo no tiene nada de mágico. La imagen de la API de Aurora Libros ya tiene dirección: auroralibros/aurora-api.

Lo único que falta es la imagen. En la siguiente lección, Construyendo Imágenes Docker, pasarás de consumir imágenes ajenas a fabricar la tuya: verás qué hace exactamente docker build, qué significa ese punto final del comando —el famoso contexto de construcción—, cómo el fichero .dockerignore que quedó prometido en 01-07 recorta ese contexto, y cómo la caché de capas de BuildKit convierte una build de un minuto en una de dos segundos si ordenas bien las instrucciones. Al terminarla tendrás auroralibros/aurora-api:0.1.0 construida en tu máquina, corriendo en un contenedor y respondiendo a curl http://localhost:3000/salud. El primer paso de los quince empieza a caer.

Docker: De Principiante a Avanzado

Módulo 1: Introducción a Docker

Módulo 2: Trabajando con Imágenes Docker

Módulo 3: Contenedores Docker

Módulo 4: Docker Compose

Módulo 5: Conceptos Avanzados de Docker

Módulo 6: Docker en Producción

Módulo 7: Ecosistema y Herramientas de Docker

© Copyright 2026. Todos los derechos reservados