Llevas tres lecciones construyendo. Cada docker build de las anteriores ha dejado capas en tu disco, y a estas alturas tu máquina acumula auroralibros/aurora-api en varias versiones, las bases node, postgres, redis, nginx y alpine, las imágenes de laboratorio de los ejercicios y una caché de BuildKit que no has mirado nunca. Esta lección trata de administrar ese almacén: saber qué tienes, por qué ocupa lo que ocupa, cómo auditar de qué está hecha una imagen, cómo borrar sin llevarte por delante lo que necesitas, de dónde salen esas misteriosas imágenes <none>:<none> que aparecen build tras build, y cómo llevarte una imagen a otra máquina cuando no hay ningún registro por medio. Es la lección menos vistosa del módulo y la que más disgustos te va a evitar: un docker system prune -a --volumes mal entendido borra en diez segundos datos que costaron semanas.

Contenido

  1. Listar imágenes: docker image ls y sus opciones
  2. Inspeccionar: docker image inspect y --format
  3. Auditar cómo se hizo: docker image history
  4. Borrar imágenes: docker image rm
  5. Imágenes dangling: las <none>:<none>
  6. Limpieza con la familia prune
  7. Diagnóstico del espacio: docker system df
  8. Portar imágenes sin registro: save/load y export/import
  9. Rutina de mantenimiento recomendada

  1. Listar imágenes: docker image ls y sus opciones

El punto de partida, con la gramática docker <objeto> <acción> de la lección 01-04:

docker image ls
REPOSITORY                TAG          IMAGE ID       CREATED          SIZE
auroralibros/aurora-api   1.1.0        4e9c7d2a8f31   10 minutes ago   167MB
auroralibros/aurora-api   1.0.0        8c1e4a7f2b9d   35 minutes ago   167MB
auroralibros/aurora-api   0.1.0        6b4d2f8e1a3c   58 minutes ago   167MB
node                      22-alpine    9f2c1a5e7b04   6 days ago       142MB
postgres                  16-alpine    b71c3d8f4a29   9 days ago       278MB
redis                     7-alpine     3e5a9c1d7b82   9 days ago       41.4MB
nginx                     alpine       c8d4f2a91e37   11 days ago      52.5MB
alpine                    3.21         a1e7f9c34d02   3 weeks ago      8.17MB
registry                  2            7f1e2b8c5a43   2 months ago     25.4MB

docker images es el alias corto y hace exactamente lo mismo.

Un aviso fundamental sobre la columna SIZE, que ya intuiste en la lección 01-05: los tamaños no se suman. Las tres versiones de aurora-api dicen 167 MB cada una, pero no ocupan 501 MB en disco. Comparten la base node:22-alpine y buena parte de sus capas; el espacio real lo dirá docker system df en el apartado 7.

Opciones de listado

# -a: incluye las capas intermedias (con el constructor clásico)
docker image ls -a

# --digests: muestra el digest sha256 de cada imagen
docker image ls --digests node

# -q: solo los IDs, ideal para encadenar comandos
docker image ls -q

# --no-trunc: IDs completos, sin abreviar
docker image ls --no-trunc
docker image ls --digests auroralibros/aurora-api
REPOSITORY                TAG     DIGEST                              IMAGE ID       SIZE
auroralibros/aurora-api   1.1.0   <none>                              4e9c7d2a8f31   167MB
auroralibros/aurora-api   1.0.0   <none>                              8c1e4a7f2b9d   167MB

El digest sale <none> porque estas imágenes se construyeron en local y nunca se han publicado: el digest del manifiesto lo asigna el registro al recibir el push. En cuanto las publiques en la lección 02-06, esa columna se rellenará.

Filtrar con --filter

# Imágenes de un repositorio concreto
docker image ls auroralibros/aurora-api

# Con comodín en el nombre
docker image ls "auroralibros/*"

# Solo las dangling (apartado 5)
docker image ls --filter "dangling=true"

# Anteriores a una imagen dada
docker image ls --filter "before=auroralibros/aurora-api:1.0.0"

# Posteriores a una imagen dada
docker image ls --filter "since=node:22-alpine"

# Por etiqueta OCI, las que añadiste en 02-04
docker image ls --filter "label=org.opencontainers.image.vendor=Aurora Libros S.L."
REPOSITORY                TAG     IMAGE ID       CREATED          SIZE
auroralibros/aurora-api   1.1.0   4e9c7d2a8f31   12 minutes ago   167MB

Solo aparece la 1.1.0, porque es la única que lleva las etiquetas OCI. Aquí se ve el rendimiento práctico del trabajo de la lección anterior: los metadatos no eran decoración, son un índice consultable.

Formato personalizado con plantillas Go

Recuperando --format de la lección 01-04:

docker image ls --format "table {{.Repository}}:{{.Tag}}\t{{.Size}}\t{{.CreatedSince}}"
REPOSITORY:TAG                        SIZE      CREATED
auroralibros/aurora-api:1.1.0         167MB     13 minutes ago
auroralibros/aurora-api:1.0.0         167MB     38 minutes ago
node:22-alpine                        142MB     6 days ago

Campos disponibles: .ID, .Repository, .Tag, .Digest, .CreatedSince, .CreatedAt, .Size.

Sin la palabra table, la salida es texto plano, perfecto para scripts:

docker image ls --format "{{.Repository}}:{{.Tag}}" --filter "reference=auroralibros/*"
auroralibros/aurora-api:1.1.0
auroralibros/aurora-api:1.0.0
auroralibros/aurora-api:0.1.0

Y en JSON, para procesar con jq:

docker image ls --format json | head -1
{"Containers":"N/A","CreatedAt":"2026-08-04 11:42:03 +0200 CEST","CreatedSince":"14 minutes ago","Digest":"<none>","ID":"4e9c7d2a8f31","Repository":"auroralibros/aurora-api","Size":"167MB","Tag":"1.1.0"}

  1. Inspeccionar: docker image inspect y --format

docker image inspect vuelca todos los metadatos de una imagen en JSON. Sin filtro son cientos de líneas:

docker image inspect auroralibros/aurora-api:1.1.0 | wc -l
187

La gracia está en extraer solo lo que necesitas. La sintaxis de --format es la misma de la lección 01-04, y estos son los campos que más vas a usar:

# El proceso que arranca el contenedor
docker image inspect auroralibros/aurora-api:1.1.0 --format '{{json .Config.Entrypoint}} {{json .Config.Cmd}}'
["node"] ["server.js"]
# Variables de entorno, una por línea
docker image inspect auroralibros/aurora-api:1.1.0 --format '{{range .Config.Env}}{{println .}}{{end}}'
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
NODE_VERSION=22.14.0
YARN_VERSION=1.22.22
NODE_ENV=production
PORT=3000
APP_VERSION=1.1.0

Este es el comando con el que auditas que no hay ningún secreto dentro de una imagen antes de publicarla. Conviértelo en un reflejo.

# Usuario, directorio de trabajo y puertos declarados
docker image inspect auroralibros/aurora-api:1.1.0 \
  --format 'Usuario:  {{.Config.User}}
WorkDir:  {{.Config.WorkingDir}}
Puertos:  {{range $p, $_ := .Config.ExposedPorts}}{{$p}} {{end}}'
Usuario:  node
WorkDir:  /app
Puertos:  3000/tcp
# Arquitectura y sistema operativo: crítico en máquinas ARM
docker image inspect node:22-alpine --format '{{.Os}}/{{.Architecture}} · Docker {{.DockerVersion}}'
linux/amd64 · Docker 27.4.1

Si esto dijera linux/amd64 y estuvieras en un Mac con Apple Silicon, la imagen funcionaría por emulación, mucho más lenta. Es la primera comprobación ante un contenedor inexplicablemente lento.

# Las capas: los digests del sistema de archivos
docker image inspect auroralibros/aurora-api:1.1.0 --format '{{range .RootFS.Layers}}{{println .}}{{end}}'
sha256:4a1b8c2f9e3d7a5b1c8f2e6d4a9b3c7e1f5d8a2b6c4e9f3a7d1b5c8e2f6a4d9b
sha256:8f3e1d7c5b9a2f4e8d6c1b3a7f9e5d2c8b4a6f1e3d7c9b5a2f8e4d6c1b3a7f9e
...
# Cuenta rápida de capas: buen indicador de complejidad
docker image inspect auroralibros/aurora-api:1.1.0 --format '{{len .RootFS.Layers}} capas'
7 capas
# Las etiquetas OCI de la lección 02-04
docker image inspect auroralibros/aurora-api:1.1.0 \
  --format '{{index .Config.Labels "org.opencontainers.image.revision"}}'
7a3f912

Un comando, y ya sabes exactamente qué commit lleva dentro esa imagen.

Puedes inspeccionar varias a la vez y comparar:

docker image inspect --format '{{.RepoTags}} → {{.Size}} bytes, {{len .RootFS.Layers}} capas' \
  auroralibros/aurora-api:1.1.0 node:22-alpine alpine:3.21
[auroralibros/aurora-api:1.1.0] → 167142891 bytes, 7 capas
[node:22-alpine] → 142331904 bytes, 4 capas
[alpine:3.21] → 8172544 bytes, 1 capas

Ahí se ve la genealogía completa: Alpine aporta 1 capa, Node añade 3 más, y tu Dockerfile las 3 restantes (los dos COPY y el RUN).

  1. Auditar cómo se hizo: docker image history

docker image history reconstruye la historia de construcción de una imagen capa a capa. Es la herramienta de auditoría por excelencia y ya la usaste en la lección 02-01 para evaluar imágenes ajenas.

docker image history auroralibros/aurora-api:1.1.0
IMAGE          CREATED          CREATED BY                                      SIZE      COMMENT
4e9c7d2a8f31   18 minutes ago   CMD ["server.js"]                               0B        buildkit.dockerfile.v0
<missing>      18 minutes ago   ENTRYPOINT ["node"]                             0B        buildkit.dockerfile.v0
<missing>      18 minutes ago   HEALTHCHECK &{["CMD-SHELL" "wget --quiet --…    0B        buildkit.dockerfile.v0
<missing>      18 minutes ago   EXPOSE map[3000/tcp:{}]                         0B        buildkit.dockerfile.v0
<missing>      18 minutes ago   USER node                                       0B        buildkit.dockerfile.v0
<missing>      18 minutes ago   ENV NODE_ENV=production PORT=3000 APP_VERSI…    0B        buildkit.dockerfile.v0
<missing>      18 minutes ago   COPY . . # buildkit                             52.1kB    buildkit.dockerfile.v0
<missing>      18 minutes ago   RUN /bin/sh -c npm ci --omit=dev && npm cac…    24.7MB    buildkit.dockerfile.v0
<missing>      18 minutes ago   COPY package*.json ./ # buildkit                44.6kB    buildkit.dockerfile.v0
<missing>      18 minutes ago   WORKDIR /app                                    0B        buildkit.dockerfile.v0
<missing>      18 minutes ago   LABEL org.opencontainers.image.title=auror…     0B        buildkit.dockerfile.v0
<missing>      6 days ago       CMD ["node"]                                    0B        buildkit.dockerfile.v0
<missing>      6 days ago       ENTRYPOINT ["docker-entrypoint.sh"]             0B        buildkit.dockerfile.v0
<missing>      6 days ago       RUN /bin/sh -c apk add --no-cache --virtual…    7.82MB    buildkit.dockerfile.v0
<missing>      6 days ago       ENV NODE_VERSION=22.14.0                        0B        buildkit.dockerfile.v0
<missing>      6 days ago       /bin/sh -c #(nop) ADD file:1b8a2c9e4d7f… in /   8.17MB

Cómo leerlo:

  • Se lee de abajo arriba. La última línea es la primera capa (el sistema de archivos base de Alpine); la primera línea es la instrucción más reciente.
  • <missing> no es un error. Solo la capa superior tiene un ID propio; las intermedias de una imagen construida con BuildKit no lo exponen. Es normal.
  • La columna SIZE es la que importa. Ahí se ve que el RUN npm ci aporta 24,7 MB y los dos COPY apenas 52 kB y 44 kB juntos. Todos los metadatos —ENV, EXPOSE, USER, LABEL, HEALTHCHECK, ENTRYPOINT, CMD— pesan 0 B, exactamente como se anunció en la lección anterior.
  • La historia incluye la de la imagen base. Las líneas de "6 días" son de node:22-alpine: ves cómo la construyeron sus mantenedores.

Localizar la capa que más pesa

Es el uso más rentable del comando:

docker image history auroralibros/aurora-api:1.1.0 --format "{{.Size}}\t{{.CreatedBy}}" --no-trunc | sort -rh | head -5
24.7MB   RUN /bin/sh -c npm ci --omit=dev && npm cache clean --force # buildkit
8.17MB   /bin/sh -c #(nop) ADD file:1b8a2c9e4d7f... in /
7.82MB   RUN /bin/sh -c apk add --no-cache --virtual .build-deps ...
52.1kB   COPY . . # buildkit
44.6kB   COPY package*.json ./ # buildkit

Cuando una imagen pesa 900 MB y no sabes por qué, este comando te da la respuesta en un segundo. Los sospechosos habituales: cachés de gestores de paquetes sin limpiar, herramientas de compilación que se quedaron dentro y ficheros temporales borrados en otra capa (con el efecto nulo que demostraste en la lección 02-03).

--no-trunc: ver el comando completo

Por defecto la columna CREATED BY se corta. Para auditar de verdad, hace falta el texto entero:

docker image history node:22-alpine --no-trunc --format "{{.CreatedBy}}" | head -3

Este es el comando con el que detectas en una imagen ajena un curl … | sh sospechoso, la instalación de una herramienta que no esperabas o, en tu propia imagen antes de publicarla, un --build-arg con una contraseña como el de la lección 02-04.

Una limitación honesta: history muestra las instrucciones, no el contenido. Un fichero copiado con COPY no se ve aquí. Para eso, arrancar la imagen y mirar (docker run --rm imagen ls -la /app) o herramientas de terceros como dive (lección 07-04).

  1. Borrar imágenes: docker image rm

docker image rm auroralibros/aurora-api:0.1.0
Untagged: auroralibros/aurora-api:0.1.0
Deleted: sha256:6b4d2f8e1a3c...
Deleted: sha256:9a2f7c1e5b8d...

docker rmi es el alias corto. Fíjate en la salida: Untagged y Deleted son dos cosas distintas, y entender la diferencia es la clave de todo este apartado.

Borrar una de varias etiquetas

Cuando dos etiquetas apuntan a la misma imagen (el mismo IMAGE ID), borrar una no borra la imagen:

docker image tag auroralibros/aurora-api:1.1.0 auroralibros/aurora-api:estable
docker image ls auroralibros/aurora-api
REPOSITORY                TAG       IMAGE ID       SIZE
auroralibros/aurora-api   1.1.0     4e9c7d2a8f31   167MB
auroralibros/aurora-api   estable   4e9c7d2a8f31   167MB

Mismo IMAGE ID: es una imagen con dos nombres, no dos imágenes de 167 MB. Ahora borra una:

docker image rm auroralibros/aurora-api:estable
Untagged: auroralibros/aurora-api:estable

Solo Untagged, sin ningún Deleted. Se ha quitado el nombre; los datos siguen ahí porque 1.1.0 los sigue referenciando. El Deleted solo aparece cuando desaparece la última referencia. Esta es la razón de que a veces borres una imagen y no recuperes ni un byte de disco.

Borrar por ID y por digest

# Por ID (basta un prefijo inequívoco)
docker image rm 4e9c7d2a

# Por digest, tras haber publicado la imagen
docker image rm node@sha256:9f2c1a5e7b04...

Borrar por ID cuando hay varias etiquetas apuntando a esa imagen falla:

Error response from daemon: conflict: unable to delete 4e9c7d2a8f31 (must be forced)
- image is referenced in multiple repositories

Docker se niega porque no sabe qué nombre quieres eliminar. O borras cada etiqueta por su nombre, o usas -f para eliminarlas todas de golpe.

El error de la imagen en uso

El más frecuente de todos:

docker run -d --name aurora-demo auroralibros/aurora-api:1.1.0
docker stop aurora-demo
docker image rm auroralibros/aurora-api:1.1.0
Error response from daemon: conflict: unable to remove repository reference
"auroralibros/aurora-api:1.1.0" (must force) - container 3f8a1c9b7e2d is using its
referenced image 4e9c7d2a8f31

El contenedor está parado, no en ejecución, y aun así bloquea el borrado. Tiene toda la lógica: como aprendiste en la lección 01-05, un contenedor parado conserva su capa de escritura, que se apila encima de las capas de la imagen. Borrar la imagen dejaría esa capa flotando sobre la nada, y el contenedor no podría volver a arrancar.

Las tres salidas posibles:

# a) Localizar los contenedores que la usan
docker ps -a --filter ancestor=auroralibros/aurora-api:1.1.0

# b) Borrar el contenedor y luego la imagen — LA FORMA CORRECTA
docker rm aurora-demo
docker image rm auroralibros/aurora-api:1.1.0

# c) Forzar
docker image rm -f auroralibros/aurora-api:1.1.0

¿Cuándo es aceptable -f? Menos veces de las que se usa:

Situación ¿-f? Alternativa
Contenedor parado que ya no necesitas Aceptable Mejor docker rm primero: más explícito
Varias etiquetas apuntando a la misma imagen y quieres borrarlas todas Es el uso legítimo
Contenedor en ejecución No Párala antes, con su ciclo de vida ordenado
No sabes por qué falla No Investiga con docker ps -a --filter ancestor=…

El peligro de -f sobre un contenedor en ejecución: la imagen queda "borrada" del listado pero sus capas siguen ocupando disco mientras el contenedor viva, y el contenedor no podrá reiniciarse cuando se pare. Acabas con un servicio que funciona hasta el primer reinicio y luego es irrecuperable, sin ninguna forma de reconstruirlo salvo el registro. Es un incidente de producción clásico.

Borrado masivo

# Todas las imágenes de un repositorio
docker image rm $(docker image ls -q auroralibros/aurora-api)

# TODAS las imágenes (¡cuidado!)
docker image rm -f $(docker image ls -aq)

El patrón $(docker image ls -q …) combina -q (solo IDs) con la sustitución de comandos del shell. Antes de ejecutar un borrado masivo, ejecuta primero solo la parte interna para ver qué vas a destruir:

docker image ls auroralibros/aurora-api    # Mira qué hay
docker image rm $(docker image ls -q auroralibros/aurora-api)   # Y entonces borra

  1. Imágenes dangling: las <none>:<none>

Tarde o temprano verás esto:

REPOSITORY                TAG       IMAGE ID       CREATED          SIZE
<none>                    <none>    2f8e1a9c4b73   5 minutes ago    167MB
<none>                    <none>    7d3c9f2e8a51   22 minutes ago   167MB
auroralibros/aurora-api   1.1.0     4e9c7d2a8f31   30 minutes ago   167MB

Esas <none>:<none> son imágenes dangling (colgantes): imágenes reales, completas y perfectamente funcionales, que han perdido su nombre.

De dónde salen

La causa principal, con diferencia, es reconstruir con la misma etiqueta:

cd ~/aurora-libros/api
docker build -t auroralibros/aurora-api:1.1.0 .    # Imagen A ← 1.1.0
echo "// un cambio" >> server.js
docker build -t auroralibros/aurora-api:1.1.0 .    # Imagen B ← 1.1.0

En la segunda build nace una imagen nueva (contenido distinto, ID distinto) y la etiqueta 1.1.0 se mueve a ella. La imagen A no se borra: se queda sin nombre. Es exactamente el modelo de la lección 02-01 —las etiquetas son punteros móviles a digests inmutables—, visto desde el lado local.

Compruébalo:

docker image ls --filter "dangling=true"
REPOSITORY   TAG       IMAGE ID       CREATED         SIZE
<none>       <none>    2f8e1a9c4b73   2 minutes ago   167MB

Las otras dos fuentes:

  • Builds sin -t. docker build . produce una imagen sin nombre desde el primer segundo. Es el motivo del consejo de la lección 02-02: etiqueta siempre.
  • Builds fallidas que dejaron capas intermedias.

Por qué importan

Porque se acumulan en silencio. Un desarrollador que reconstruye veinte veces al día con la misma etiqueta genera veinte imágenes dangling diarias. La mayoría comparten capas y no ocupan 167 MB cada una, pero las capas propias (el COPY . . y a veces el RUN npm ci) sí son exclusivas. En unas semanas son varios gigabytes, y el mensaje no space left on device llega en el peor momento.

No son basura absoluta: si sabes su ID, puedes ejecutarlas y hasta reetiquetarlas.

docker image tag 2f8e1a9c4b73 recuperada:1.0

Eso salva la papeleta cuando borras por error la etiqueta de una imagen que aún no habías publicado. Pero como práctica habitual, se limpian.

  1. Limpieza con la familia prune

Cuatro comandos, con cuatro radios de destrucción muy distintos. Esta tabla es la que hay que memorizar:

Comando Qué borra Riesgo
docker image prune Solo imágenes dangling Bajo: es seguro casi siempre
docker image prune -a Todas las imágenes sin ningún contenedor que las use Medio: te obliga a redescargar bases
docker builder prune La caché de BuildKit Medio: las builds siguientes serán lentas
docker system prune Contenedores parados + redes sin usar + dangling + caché de build Medio-alto
docker system prune -a --volumes Todo lo anterior + todas las imágenes sin contenedor + volúmenes MUY ALTO: destruye datos

docker image prune

docker image prune
WARNING! This will remove all dangling images.
Are you sure you want to continue? [y/N] y
Deleted Images:
deleted: sha256:2f8e1a9c4b73...
deleted: sha256:7d3c9f2e8a51...

Total reclaimed space: 51.3MB

Este es el que puedes ejecutar con confianza: solo se lleva imágenes sin nombre. Con -f se salta la confirmación (útil en scripts) y con --filter "until=168h" limita el borrado a las de más de una semana:

docker image prune -f --filter "until=168h"

docker image prune -a

docker image prune -a
WARNING! This will remove all images without at least one container associated to them.
Are you sure you want to continue? [y/N]

Lee bien el aviso: borra todas las imágenes que no tengan un contenedor asociado, no solo las dangling. Si no tienes contenedores creados —lo normal después de limpiar—, esto se lleva node:22-alpine, postgres:16-alpine, redis:7-alpine, tus aurora-api locales y todo lo demás. No es catastrófico (se puede reconstruir y redescargar), pero significa gigabytes de descarga y, si estás sin conexión o cerca del límite de descargas de la lección 02-01, un mal rato.

docker builder prune

La caché de BuildKit es invisible en docker image ls y suele ser lo más voluminoso de la máquina:

docker builder prune
WARNING! This will remove all dangling build cache. Are you sure you want to continue? [y/N] y
Total:  2.847GB

Casi tres gigas que no aparecían en ningún listado de imágenes. Variantes:

docker builder prune -a                      # También la caché en uso
docker builder prune --filter "until=72h"    # Solo lo anterior a 3 días
docker builder prune --keep-storage=10GB     # Deja hasta 10 GB de caché

La contrapartida: la siguiente build de cada proyecto irá en frío. Recuerda de la lección 02-02 que ahí es donde la caché no ayuda.

docker system prune

docker system prune
WARNING! This will remove:
  - all stopped containers
  - all networks not used by at least one container
  - all dangling images
  - unused build cache

Are you sure you want to continue? [y/N] y

Deleted Containers:
3f8a1c9b7e2d...
Deleted Networks:
aurora-red-pruebas
Deleted Images:
deleted: sha256:2f8e1a9c4b73...
Total reclaimed space: 3.12GB

El aviso lista exactamente lo que va a borrar. Léelo siempre: es la diferencia entre limpiar y perder trabajo. Lo que se lleva por sorpresa suele ser un contenedor parado que contenía datos en su capa de escritura, o una red que habías creado a mano para unas pruebas.

docker system prune -a --volumes: el comando peligroso

docker system prune -a --volumes
WARNING! This will remove:
  - all stopped containers
  - all networks not used by at least one container
  - all volumes not used by at least one container
  - all images without at least one container associated to them
  - all build cache

Esta es la línea que arruina el día: all volumes not used by at least one container.

Traducido a Aurora Libros: cuando en el módulo 3 tengas un volumen con el catálogo de PostgreSQL y hayas parado y borrado el contenedor de aurora-db para recrearlo, ese volumen queda temporalmente sin contenedor asociado. Un docker system prune -a --volumes en ese momento borra la base de datos entera. Sin papelera, sin deshacer, sin recuperación.

Reglas de uso:

  • Nunca en un servidor. Nunca.
  • En desarrollo, solo cuando tengas claro que no hay ningún volumen que te importe.
  • Antes de ejecutarlo, mira qué volúmenes hay:
docker volume ls
docker system df -v | head -20
  • Si solo quieres espacio, la escalera segura es: docker image prunedocker builder prunedocker system prune. Rara vez hace falta llegar al último escalón.

  1. Diagnóstico del espacio: docker system df

Antes de borrar nada, mide.

docker system df
TYPE            TOTAL     ACTIVE    SIZE      RECLAIMABLE
Images          9         2         842.3MB   601.7MB (71%)
Containers      4         1         12.4MB    9.1MB (73%)
Local Volumes   3         1         248.9MB   187.2MB (75%)
Build Cache     47        0         2.847GB   2.847GB (100%)

Cómo leerlo:

Columna Significado
TOTAL Número de objetos de ese tipo
ACTIVE Los que están en uso (imágenes con contenedor, volúmenes montados…)
SIZE Espacio real en disco, ya descontadas las capas compartidas
RECLAIMABLE Cuánto liberarías al limpiar, y qué porcentaje del total es

Dos conclusiones inmediatas de este ejemplo:

  1. Nueve imágenes ocupan 842 MB, no la suma de sus columnas SIZE en docker image ls (que daría más de 1,2 GB). La diferencia son las capas compartidas de la lección 01-05, contadas una sola vez.
  2. La caché de build es 2,85 GB, el 100 % recuperable, y es con diferencia lo más grande. Es lo primero que hay que limpiar, y no aparecía en ningún listado de imágenes.

El detalle con -v

docker system df -v
Images space usage:

REPOSITORY                TAG         IMAGE ID       SIZE      SHARED SIZE   UNIQUE SIZE   CONTAINERS
auroralibros/aurora-api   1.1.0       4e9c7d2a8f31   167MB     142MB         24.8MB        1
auroralibros/aurora-api   1.0.0       8c1e4a7f2b9d   167MB     142MB         24.7MB        0
node                      22-alpine   9f2c1a5e7b04   142MB     142MB         0B            0
postgres                  16-alpine   b71c3d8f4a29   278MB     8.17MB        270MB         0

Containers space usage:

CONTAINER ID   IMAGE                           SIZE      STATUS
3f8a1c9b7e2d   auroralibros/aurora-api:1.1.0   4.2MB     Up 12 minutes

Local Volumes space usage:

VOLUME NAME                                   LINKS     SIZE
aurora-datos-db                               1         187.2MB
f3a9c2e1b8d7460a2c5f8e1d4b7a3c9e2f6d8a1b     0         61.7MB

Las columnas SHARED SIZE y UNIQUE SIZE son las que aclaran todo:

  • node:22-alpine: 142 MB de tamaño, 142 MB compartidos, 0 B únicos. Borrarla no liberaría nada, porque todas sus capas las usan tus imágenes de aurora-api.
  • aurora-api:1.0.0: 167 MB, de los cuales solo 24,7 MB son exclusivos. Eso es lo que ganarías al borrarla.
  • postgres:16-alpine: 278 MB con 270 MB únicos. Esta sí es un buen candidato si no la vas a usar.

Y mira la última línea de volúmenes: f3a9c2e1b8d7… con 0 links es un volumen anónimo huérfano, exactamente lo que anticipaba la lección 02-04 al desaconsejar VOLUME en el Dockerfile. Ocupa 61,7 MB y nadie sabe qué contiene.

  1. Portar imágenes sin registro: save/load y export/import

A veces hay que llevar una imagen a otra máquina sin registro por medio: un cliente con la red aislada, un entorno sin salida a Internet, una demo en un portátil sin conexión. Docker ofrece dos parejas de comandos que se confunden constantemente y que no son intercambiables.

Aspecto save / load export / import
Opera sobre Una imagen Un contenedor
Qué guarda Todas las capas + manifiesto + metadatos El sistema de archivos aplanado, sin capas
Conserva el historial No
Conserva CMD, ENTRYPOINT, ENV, USER, EXPOSE No
Conserva etiquetas de imagen No (hay que reetiquetar)
Varias imágenes en un fichero No
Tamaño del .tar Mayor (todas las capas) Menor (un solo nivel)
Uso correcto Portar imágenes Extraer un sistema de archivos, aplanar

La regla: para mover una imagen, siempre save/load. export/import sirve para otra cosa.

docker save / docker load con Aurora Libros

# 1. Exportar la imagen a un fichero tar
docker save -o aurora-api-1.1.0.tar auroralibros/aurora-api:1.1.0
ls -lh aurora-api-1.1.0.tar
-rw------- 1 joan joan 168M ago  4 12:14 aurora-api-1.1.0.tar
# 2. Comprimir: las capas son sobre todo texto y binarios, comprimen bien
gzip -9 aurora-api-1.1.0.tar
ls -lh aurora-api-1.1.0.tar.gz
-rw------- 1 joan joan 62M ago  4 12:15 aurora-api-1.1.0.tar.gz

De 168 MB a 62 MB: un 63 % menos, y eso es lo que viaja por el pendrive o el SCP. También se puede hacer en un solo paso con una tubería:

docker save auroralibros/aurora-api:1.1.0 | gzip -9 > aurora-api-1.1.0.tar.gz
# 3. Transferir a la otra máquina
scp aurora-api-1.1.0.tar.gz operador@servidor-aurora:/tmp/

# 4. Cargar allí
ssh operador@servidor-aurora
gunzip -c /tmp/aurora-api-1.1.0.tar.gz | docker load
Loaded image: auroralibros/aurora-api:1.1.0
# 5. Verificar que llegó completa
docker image ls auroralibros/aurora-api
docker image inspect auroralibros/aurora-api:1.1.0 --format '{{json .Config.Entrypoint}} · {{.Config.User}}'
docker run -d --name aurora-portada -p 3000:3000 auroralibros/aurora-api:1.1.0
curl -s http://localhost:3000/salud
REPOSITORY                TAG     IMAGE ID       CREATED          SIZE
auroralibros/aurora-api   1.1.0   4e9c7d2a8f31   45 minutes ago   167MB

["node"] · node

{"servicio":"aurora-api","version":"1.0.0","db":"ko","cache":"ko",...}

Mismo IMAGE ID, mismo ENTRYPOINT, mismo USER. La imagen ha llegado íntegra, con todos los metadatos que definiste en la lección 02-04.

Guardar varias imágenes en un solo fichero, útil para llevarse la pila entera de Aurora Libros:

docker save -o aurora-pila.tar \
  auroralibros/aurora-api:1.1.0 \
  postgres:16-alpine \
  redis:7-alpine \
  nginx:alpine
ls -lh aurora-pila.tar
-rw------- 1 joan joan 512M ago  4 12:22 aurora-pila.tar

Un único fichero con todo lo necesario para levantar la plataforma en una máquina sin Internet.

docker export / docker import

Trabaja sobre contenedores, no imágenes, y aplana el resultado:

docker run -d --name para-exportar auroralibros/aurora-api:1.1.0
docker export -o aurora-plano.tar para-exportar
ls -lh aurora-plano.tar
-rw------- 1 joan joan 158M ago  4 12:25 aurora-plano.tar
docker import aurora-plano.tar aurora-plana:1.0
docker image history aurora-plana:1.0
IMAGE          CREATED         CREATED BY   SIZE      COMMENT
5c9e2f7a1b83   4 seconds ago                158MB     Imported from -

Una sola capa y ningún historial. Toda la genealogía ha desaparecido. Y lo grave:

docker run --rm aurora-plana:1.0
docker: Error response from daemon: no command specified.

Se han perdido el ENTRYPOINT, el CMD, el ENV, el USER y el EXPOSE. La imagen importada es un sistema de archivos sin instrucciones. Habría que reponerlos a mano al ejecutar:

docker run --rm -u node -e NODE_ENV=production -w /app aurora-plana:1.0 node server.js

Entonces, ¿para qué sirve? Para dos cosas legítimas:

  1. Aplanar una imagen con demasiadas capas o con un secreto enterrado en una capa intermedia que quieres eliminar de verdad. Al aplanar, esa capa desaparece. (La forma moderna y mejor de conseguirlo son las builds multi-etapa, lección 05-04.)
  2. Extraer el sistema de archivos de un contenedor para analizarlo forensemente o para construir una imagen base a partir de un sistema existente.

Se pueden reponer los metadatos al importar, con -c:

docker import \
  -c 'ENTRYPOINT ["node"]' \
  -c 'CMD ["server.js"]' \
  -c 'WORKDIR /app' \
  -c 'USER node' \
  -c 'ENV NODE_ENV=production PORT=3000' \
  -c 'EXPOSE 3000' \
  aurora-plano.tar aurora-plana:1.1
docker run -d --name plana-ok -p 3001:3000 aurora-plana:1.1
curl -s http://localhost:3001/salud | head -c 60
{"servicio":"aurora-api","version":"1.0.0","db":"ko","cache":"ko"

Funciona, pero has tenido que reconstruir a mano lo que save conservaba solo. Queda claro cuál es la herramienta adecuada para portar imágenes.

Limpieza:

docker rm -f para-exportar plana-ok aurora-portada 2>/dev/null
docker image rm aurora-plana:1.0 aurora-plana:1.1 2>/dev/null
rm -f aurora-plano.tar

  1. Rutina de mantenimiento recomendada

Con todo lo anterior, esta es una rutina razonable para una máquina de desarrollo.

Semanal (seguro, se puede automatizar):

docker image prune -f
docker builder prune -f --filter "until=168h"
docker system df

Borra las dangling y la caché de build de más de una semana, y muestra cómo queda el espacio. No toca nada con nombre, ni contenedores, ni volúmenes.

Mensual (revisión manual):

docker system df -v | head -40                    # ¿Qué ocupa y qué es único?
docker image ls --filter "before=$(date -d '30 days ago' +%Y-%m-%d)" 2>/dev/null
docker volume ls -f dangling=true                 # Volúmenes huérfanos: MIRA qué son
docker ps -a --filter status=exited               # Contenedores parados olvidados

Aquí no se automatiza nada: se mira y se decide. Los volúmenes huérfanos hay que inspeccionarlos antes de borrarlos, porque pueden contener datos.

Antes de una build importante:

docker builder prune -f
docker build --no-cache --pull -t auroralibros/aurora-api:1.1.0 .

Caché limpia y base actualizada, como se explicó en la lección 02-02.

Cuando falta espacio, la escalera segura:

docker system df                                  # 1. Mide antes de tocar nada
docker builder prune -f                           # 2. Lo más grande y lo menos arriesgado
docker image prune -f                             # 3. Las dangling
docker container prune -f                         # 4. Contenedores parados (revísalos)
docker image prune -a --filter "until=720h"       # 5. Imágenes sin usar de +30 días

En cinco pasos ordenados de menor a mayor riesgo se recuperan casi siempre varios gigabytes sin llegar nunca a docker system prune -a --volumes.

Un script de mantenimiento con informe:

#!/bin/bash
# mantenimiento-docker.sh — limpieza semanal segura
set -e

echo "=== Espacio ANTES ==="
docker system df

echo "=== Limpiando imágenes dangling ==="
docker image prune -f

echo "=== Limpiando caché de build de más de 7 días ==="
docker builder prune -f --filter "until=168h"

echo "=== Espacio DESPUÉS ==="
docker system df

echo "=== Volúmenes huérfanos (REVISAR A MANO, no se borran) ==="
docker volume ls -f dangling=true

Fíjate en la última sección: lista los volúmenes huérfanos pero no los borra. Esa es la línea que separa un script de mantenimiento de un script de destrucción de datos.

Errores Comunes y Consejos

  • Sumar la columna SIZE de docker image ls. No es espacio en disco: las capas compartidas se cuentan en cada fila. El dato real está en docker system df, y el desglose en docker system df -v.
  • Borrar una etiqueta y esperar recuperar espacio. Si la salida solo dice Untagged y no Deleted, no has liberado nada: la imagen tiene otra referencia.
  • docker image rm -f sobre un contenedor en ejecución. La imagen "desaparece" pero sus capas siguen en disco y el contenedor no podrá reiniciarse nunca más. Para el contenedor primero.
  • docker system prune -a --volumes sin leer el aviso. Borra volúmenes sin contenedor asociado, y un volumen de base de datos entre recreaciones de contenedor está exactamente en ese estado. Nunca en un servidor.
  • Ignorar la caché de BuildKit. No aparece en docker image ls y suele ser lo más voluminoso de la máquina. docker system df la delata.
  • Confundir save/load con export/import. export pierde ENTRYPOINT, CMD, ENV, USER y el historial, y la imagen importada da no command specified. Para portar imágenes, siempre save/load.
  • Olvidar comprimir el .tar. Un gzip -9 recorta habitualmente entre un 50 % y un 65 %.
  • Consejo: audita las variables antes de publicar. docker image inspect --format '{{range .Config.Env}}{{println .}}{{end}}' debe ser un reflejo antes de cualquier docker push.
  • Consejo: usa docker image history … | sort -rh para cazar capas gordas. Es el diagnóstico de "por qué pesa tanto esta imagen" en un solo comando.
  • Consejo: etiqueta siempre con -t. Cada build sin etiqueta es una imagen dangling desde el primer segundo.
  • Consejo: antes de un borrado masivo, ejecuta solo la parte interna del $( … ) para ver qué vas a destruir.

Ejercicios

Ejercicio 1: audita el espacio real de tu máquina

  1. Ejecuta docker system df y anota el total y el porcentaje recuperable de cada categoría.
  2. Con docker system df -v, identifica la imagen con mayor UNIQUE SIZE y la que tenga UNIQUE SIZE de 0 B. Explica qué significa cada caso y cuánto liberaría borrar cada una.
  3. Localiza con docker image history la capa más pesada de auroralibros/aurora-api:1.1.0 y de postgres:16-alpine.
  4. Comprueba si tienes volúmenes huérfanos y averigua, sin borrarlos, de qué imagen salieron.

Ejercicio 2: demuestra el ciclo de vida de las etiquetas y las dangling

  1. Construye auroralibros/aurora-api:experimento desde tu Dockerfile y anota el IMAGE ID.
  2. Crea una segunda etiqueta auroralibros/aurora-api:copia apuntando a la misma imagen. Comprueba que el ID coincide y que docker system df no ha crecido.
  3. Borra la etiqueta copia. ¿Sale Untagged, Deleted o ambos? ¿Por qué?
  4. Modifica server.js, reconstruye con la misma etiqueta experimento y comprueba que aparece una imagen <none>:<none>. Explica de dónde salió.
  5. Recupera esa imagen dangling reetiquetándola como auroralibros/aurora-api:rescatada y verifica que funciona.
  6. Limpia todo lo creado en el ejercicio.

Ejercicio 3: porta la pila de Aurora Libros a una máquina sin Internet

Simula un despliegue en un entorno aislado:

  1. Exporta en un único fichero auroralibros/aurora-api:1.1.0, postgres:16-alpine y redis:7-alpine.
  2. Comprímelo y anota el tamaño antes y después.
  3. Borra las tres imágenes de tu máquina (simulando la máquina destino en blanco).
  4. Cárgalas desde el fichero y verifica que aurora-api conserva ENTRYPOINT, USER, ENV y el HEALTHCHECK.
  5. Repite el ciclo con export/import sobre un contenedor de aurora-api y compara: tamaño del tar, número de capas del history y comportamiento al ejecutar docker run sin argumentos. Concluye qué pareja usarías y por qué.

Soluciones

Solución al ejercicio 1

docker system df
TYPE            TOTAL     ACTIVE    SIZE      RECLAIMABLE
Images          9         2         842.3MB   601.7MB (71%)
Containers      4         1         12.4MB    9.1MB (73%)
Local Volumes   3         1         248.9MB   187.2MB (75%)
Build Cache     47        0         2.847GB   2.847GB (100%)

1. Total aproximado: 3,95 GB, de los cuales 3,65 GB son recuperables. La caché de build es el 72 % de todo y es 100 % recuperable: es el objetivo prioritario.

2.

docker system df -v | head -15
  • Mayor UNIQUE SIZE: postgres:16-alpine, con 270 MB únicos de sus 278 MB. Solo comparte con las demás la capa base de Alpine (8,17 MB). Borrarla liberaría 270 MB reales.
  • UNIQUE SIZE de 0 B: node:22-alpine. Sus 142 MB están íntegramente compartidos con tus imágenes de aurora-api, que se construyeron sobre ella. Borrarla no liberaría ni un byte mientras exista alguna imagen derivada; solo desaparecería del listado. Es la demostración práctica de las capas compartidas de la lección 01-05.

3.

docker image history auroralibros/aurora-api:1.1.0 --format "{{.Size}}\t{{.CreatedBy}}" | sort -rh | head -3
docker image history postgres:16-alpine --format "{{.Size}}\t{{.CreatedBy}}" --no-trunc | sort -rh | head -3
24.7MB   RUN /bin/sh -c npm ci --omit=dev && npm cache clean --force # buildkit
8.17MB   /bin/sh -c #(nop) ADD file:1b8a2c9e... in /
7.82MB   RUN /bin/sh -c apk add --no-cache --virtual .build-deps ...

248MB    RUN /bin/sh -c set -eux; apk add --no-cache --virtual .build-deps ... ; make -C /usr/src/postgresql ...
21.4MB   RUN /bin/sh -c apk add --no-cache bash su-exec tzdata zstd
8.17MB   /bin/sh -c #(nop) ADD file:1b8a2c9e... in /

En aurora-api, el npm ci con 24,7 MB es el mayor contribuyente propio: son las dependencias de producción. En postgres, los 248 MB de la compilación de PostgreSQL explican por sí solos su tamaño.

4.

docker volume ls -f dangling=true
docker volume inspect f3a9c2e1b8d7460a2c5f8e1d4b7a3c9e2f6d8a1b --format '{{.Mountpoint}} · creado {{.CreatedAt}}'
sudo ls /var/lib/docker/volumes/f3a9c2e1b8d7460a2c5f8e1d4b7a3c9e2f6d8a1b/_data
PG_VERSION  base  global  pg_wal  postgresql.conf  ...

El contenido delata su origen: es un volumen anónimo creado por la imagen de PostgreSQL, que declara VOLUME /var/lib/postgresql/data en su Dockerfile. Es exactamente el escenario que la lección 02-04 daba como razón para no usar VOLUME. No lo borres sin comprobar el contenido: podría ser una base de datos con trabajo dentro.

Solución al ejercicio 2

cd ~/aurora-libros/api

# 1
docker build -q -t auroralibros/aurora-api:experimento .
docker image ls auroralibros/aurora-api:experimento --format "{{.ID}}"
4e9c7d2a8f31
# 2
docker system df --format "{{.Type}}: {{.Size}}" | head -1
docker image tag auroralibros/aurora-api:experimento auroralibros/aurora-api:copia
docker image ls auroralibros/aurora-api --format "table {{.Tag}}\t{{.ID}}\t{{.Size}}"
docker system df --format "{{.Type}}: {{.Size}}" | head -1
Images: 842.3MB
TAG            ID             SIZE
experimento    4e9c7d2a8f31   167MB
copia          4e9c7d2a8f31   167MB
Images: 842.3MB

Mismo ID y el mismo espacio total. docker image tag solo crea una referencia; no copia un solo byte. Es la demostración local de lo que verás en el registro en la lección 02-06.

# 3
docker image rm auroralibros/aurora-api:copia
Untagged: auroralibros/aurora-api:copia

Solo Untagged. No hay Deleted porque la etiqueta experimento sigue apuntando a esa imagen: los datos siguen referenciados y el borrado se limita a eliminar el nombre.

# 4
echo "// cambio para generar una dangling" >> server.js
docker build -q -t auroralibros/aurora-api:experimento .
docker image ls --filter "dangling=true"
REPOSITORY   TAG       IMAGE ID       CREATED          SIZE
<none>       <none>    4e9c7d2a8f31   8 minutes ago    167MB

El ID 4e9c7d2a8f31 es el mismo del paso 1: la imagen original no se ha borrado, ha perdido el nombre. La nueva build produjo una imagen distinta y la etiqueta experimento se movió a ella. Las etiquetas son punteros, no propietarios.

# 5
docker image tag 4e9c7d2a8f31 auroralibros/aurora-api:rescatada
docker run --rm auroralibros/aurora-api:rescatada --version
docker image ls --filter "dangling=true"
v22.14.0
(sin resultados)

Recuperada y funcional, y ya no figura como dangling porque vuelve a tener nombre. Fíjate en que --version funcionó gracias al ENTRYPOINT ["node"] de la lección 02-04.

# 6
docker image rm auroralibros/aurora-api:experimento auroralibros/aurora-api:rescatada
docker image prune -f

Solución al ejercicio 3

# 1 y 2
cd /tmp
docker save -o aurora-pila.tar \
  auroralibros/aurora-api:1.1.0 postgres:16-alpine redis:7-alpine
ls -lh aurora-pila.tar
gzip -9 aurora-pila.tar
ls -lh aurora-pila.tar.gz
-rw------- 1 joan joan 481M ago  4 12:40 aurora-pila.tar
-rw------- 1 joan joan 173M ago  4 12:41 aurora-pila.tar.gz

De 481 MB a 173 MB: un 64 % menos. La compresión merece la pena siempre, sobre todo si el fichero viaja por SCP o por un pendrive.

# 3
docker image rm auroralibros/aurora-api:1.1.0 postgres:16-alpine redis:7-alpine
docker image ls | grep -E "aurora-api|postgres|redis"   # sin resultados

# 4
gunzip -c aurora-pila.tar.gz | docker load
Loaded image: auroralibros/aurora-api:1.1.0
Loaded image: postgres:16-alpine
Loaded image: redis:7-alpine
docker image inspect auroralibros/aurora-api:1.1.0 --format \
'Entrypoint: {{json .Config.Entrypoint}}
Cmd:        {{json .Config.Cmd}}
User:       {{.Config.User}}
Env:        {{index .Config.Env 3}} {{index .Config.Env 4}}
Health:     {{json .Config.Healthcheck.Test}}
Revision:   {{index .Config.Labels "org.opencontainers.image.revision"}}'
Entrypoint: ["node"]
Cmd:        ["server.js"]
User:       node
Env:        NODE_ENV=production PORT=3000
Health:     ["CMD-SHELL","wget --quiet --tries=1 --spider http://localhost:3000/salud || exit 1"]
Revision:   7a3f912

Absolutamente todo intacto: entrypoint, cmd, usuario, variables, healthcheck y hasta las etiquetas OCI con el hash del commit.

# 5. Comparación con export/import
docker run -d --name comparar auroralibros/aurora-api:1.1.0
docker export comparar | gzip -9 > aurora-plano.tar.gz
ls -lh aurora-plano.tar.gz
gunzip -c aurora-plano.tar.gz | docker import - aurora-plana:test
docker image history aurora-plana:test
docker run --rm aurora-plana:test
-rw------- 1 joan joan 58M ago  4 12:48 aurora-plano.tar.gz

IMAGE          CREATED         CREATED BY   SIZE      COMMENT
9d2e4f8a1c73   3 seconds ago                158MB     Imported from -

docker: Error response from daemon: no command specified.

Tabla comparativa final:

Aspecto save/load export/import
Tamaño comprimido (solo aurora-api) ~62 MB ~58 MB
Capas conservadas 7 1
Historial Completo Ninguno
ENTRYPOINT, CMD, USER, ENV Conservados Perdidos
HEALTHCHECK y etiquetas OCI Conservados Perdidos
docker run sin argumentos Arranca la API no command specified
Varias imágenes por fichero No

Conclusión: para portar imágenes, save/load, sin discusión. El pequeño ahorro de tamaño de export no compensa perder toda la configuración de ejecución, el healthcheck y la trazabilidad que costó una lección entera construir. export/import es una herramienta para aplanar sistemas de archivos, no para mover imágenes.

Limpieza:

docker rm -f comparar
docker image rm aurora-plana:test
rm -f /tmp/aurora-pila.tar.gz /tmp/aurora-plano.tar.gz

Conclusión

Ya sabes administrar tu almacén de imágenes. Listas y filtras con docker image ls, sus --filter (incluido el filtrado por las etiquetas OCI que añadiste en 02-04) y sus plantillas Go. Extraes cualquier metadato concreto con docker image inspect --format, y tienes un reflejo nuevo que conviene conservar: auditar las variables de entorno antes de publicar cualquier imagen. Con docker image history reconstruyes cómo se hizo una imagen, capa a capa, y localizas en un solo comando la que se comió los megas —en aurora-api, el npm ci con 24,7 MB; en postgres, los 248 MB de su compilación—.

Sabes borrar entendiendo lo que pasa: Untagged quita un nombre y Deleted destruye datos, y solo aparece cuando cae la última referencia. Sabes por qué un contenedor parado bloquea el borrado de su imagen (conserva la capa de escritura apilada encima) y cuándo -f es aceptable y cuándo es una forma elegante de romper un servicio en el próximo reinicio. Entiendes de dónde salen las <none>:<none>: reconstruir con la misma etiqueta mueve el puntero y deja huérfana a la imagen anterior, que sigue en disco y hasta se puede rescatar reetiquetándola.

Dominas la escalera de limpieza, de menor a mayor riesgo —image prune, builder prune, system prune— con una advertencia grabada a fuego sobre el último escalón: docker system prune -a --volumes borra volúmenes sin contenedor asociado, y un volumen de base de datos entre recreaciones está exactamente en ese estado. Mides antes de tocar nada con docker system df y su versión -v, que revela lo que ningún listado de imágenes muestra: la caché de BuildKit ocupando gigabytes, las capas compartidas contadas una sola vez y las columnas SHARED/UNIQUE que dicen cuánto liberarías de verdad al borrar cada imagen. Y sabes llevarte una imagen a una máquina aislada con docker save/load, conservando entrypoint, usuario, healthcheck y etiquetas OCI, frente a export/import, que aplana el sistema de archivos y lo pierde todo.

Tu imagen está construida, es profesional y sabes mantenerla. Le falta lo único que justificaba todo el módulo: salir de tu máquina. En la última lección, Etiquetado y Publicación de Imágenes, cerrarás el ciclo. Verás que docker image tag no copia nada sino que crea referencias, decidirás una estrategia de etiquetado seria —versionado semántico con etiquetas móviles y fijas, por SHA de commit, por rama, por entorno— y publicarás auroralibros/aurora-api en Docker Hub y en GitHub Container Registry, leyendo la salida del push, verificando el resultado con docker manifest inspect y entendiendo por qué en producción se despliega por digest y jamás por latest.

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