Home / DevOps & Cloud / Cómo eliminar imágenes de Docker: guía completa de limpieza
Cómo eliminar imágenes de Docker: guía completa de limpieza

Cómo eliminar imágenes de Docker: guía completa de limpieza

6 November 2026 · 18 min de lectura ··

Cuando Docker se come el disco sin avisar

Docker se ha convertido en una pieza básica del trabajo diario de cualquier equipo de desarrollo. Levantar una base de datos para pruebas, compilar una aplicación en un entorno reproducible o ejecutar una herramienta sin instalarla en el sistema son tareas que se resuelven con un solo comando. Esa comodidad tiene un coste que suele pasar desapercibido hasta que aparece el aviso de disco lleno: cada imagen descargada, cada contenedor detenido, cada volumen huérfano y cada capa de caché de compilación se queda en el disco hasta que alguien decide borrarla.

En un portátil de desarrollo no es raro que Docker acumule decenas de gigabytes en pocos meses. En un servidor de integración continua, donde se construyen imágenes varias veces al día, el problema puede aparecer en cuestión de días y provocar fallos en los pipelines con mensajes poco evidentes, como errores de escritura o compilaciones que se detienen sin una causa clara.

La buena noticia es que Docker incluye todas las herramientas necesarias para medir el espacio que ocupa y recuperarlo de forma controlada. En esta guía se explica cómo diagnosticar el consumo, qué elimina exactamente cada comando de limpieza, cómo borrar recursos de forma selectiva con filtros, cómo controlar la caché de BuildKit y los logs de los contenedores, y cómo automatizar el mantenimiento sin poner en riesgo datos importantes. Los comandos corresponden a las versiones actuales de Docker Engine, de la rama 29, y del cliente de Docker.

Qué ocupa espacio en Docker

Antes de borrar nada conviene entender qué tipos de recursos almacena Docker y por qué se acumulan. Son cinco categorías principales.

Las imágenes son las plantillas de solo lectura a partir de las cuales se crean los contenedores. Se componen de capas que pueden compartirse entre imágenes distintas. Cada vez que se descarga una nueva versión de una imagen o se construye una propia, se añaden capas al almacenamiento local.

Los contenedores son instancias de esas imágenes. Aunque un contenedor esté detenido, conserva su capa de escritura, con todos los archivos que haya creado o modificado durante su ejecución, además de sus logs.

Los volúmenes son el mecanismo de Docker para persistir datos más allá del ciclo de vida de un contenedor. Una base de datos PostgreSQL ejecutada en Docker guarda sus datos en un volumen. Al eliminar el contenedor, el volumen permanece, y es fácil que se acumulen volúmenes de proyectos que ya no existen.

La caché de compilación, gestionada por BuildKit, guarda los resultados intermedios de cada instrucción de un Dockerfile para acelerar las compilaciones posteriores. Es una de las fuentes de consumo más grandes y menos visibles.

Por último están las redes, que apenas ocupan espacio pero también se acumulan y pueden generar conflictos de rangos de direcciones cuando son muchas.

Un concepto que aparece constantemente al hablar de limpieza es el de imagen colgante, o dangling. Se trata de una imagen sin etiqueta y que no está referenciada por ninguna otra. Suelen aparecer cuando se reconstruye una imagen con el mismo nombre y etiqueta: la nueva versión se queda con la etiqueta y la antigua queda sin nombre, visible como <none>:<none>.

docker images --filter dangling=true
REPOSITORY   TAG       IMAGE ID       CREATED        SIZE
<none>       <none>    7c2d4e9f1a3b   2 days ago     412MB
<none>       <none>    e81f0a6b55d2   5 days ago     409MB

Medir antes de borrar

El primer paso de cualquier limpieza es saber cuánto espacio ocupa cada categoría. El comando docker system df ofrece un resumen inmediato:

docker system df
TYPE            TOTAL     ACTIVE    SIZE      RECLAIMABLE
Images          38        6         21.4GB    17.9GB (83%)
Containers      14        3         1.2GB     1.1GB (91%)
Local Volumes   22        4         9.8GB     7.3GB (74%)
Build Cache     311       0         14.6GB    14.6GB

La columna más útil es RECLAIMABLE, que indica cuánto espacio podría recuperarse eliminando los recursos que no están en uso. ACTIVE muestra cuántos elementos están siendo utilizados por contenedores existentes.

Para un desglose detallado, con el tamaño de cada imagen, contenedor y volumen, se añade la opción -v:

docker system df -v
Images space usage:

REPOSITORY          TAG       IMAGE ID       CREATED        SIZE      SHARED SIZE   UNIQUE SIZE   CONTAINERS
api-facturacion     latest    3f9a1c7e2b40   3 hours ago    1.12GB    184MB         936MB         1
postgres            17        a6d3e0f9c812   2 weeks ago    438MB     74.8MB        363MB         1
node                22-slim   b19c4e5a7d03   3 weeks ago    224MB     74.8MB        149MB         0

Local Volumes space usage:

VOLUME NAME                    LINKS     SIZE
facturacion_pgdata             1         2.4GB
prototipo_viejo_mongo          0         3.1GB

Las columnas SHARED SIZE y UNIQUE SIZE ayudan a entender por qué borrar una imagen no siempre libera tanto espacio como su tamaño nominal: las capas compartidas con otras imágenes siguen siendo necesarias. Solo el tamaño único se libera realmente.

En los volúmenes, la columna LINKS indica cuántos contenedores los utilizan. Un volumen con cero enlaces no está asociado a ningún contenedor, aunque eso no significa necesariamente que sus datos ya no importen.

También es útil saber dónde guarda Docker sus datos. En Linux, por defecto es /var/lib/docker, y puede consultarse con:

docker info --format '{{ .DockerRootDir }}'
/var/lib/docker

La limpieza general con docker system prune

El comando más conocido para liberar espacio es docker system prune. Sin opciones adicionales, elimina los contenedores detenidos, las redes que no usa ningún contenedor, las imágenes colgantes y la caché de compilación sin uso. Antes de actuar, muestra un resumen y pide confirmación:

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:
4e2a91c7d0f3b5...
Deleted Images:
deleted: sha256:7c2d4e9f1a3b...
Total reclaimed space: 6.82GB

Esta versión es relativamente segura: conserva todas las imágenes con etiqueta, aunque no las use ningún contenedor, y no toca los volúmenes.

Para una limpieza más profunda existe la opción -a (o --all), que elimina también todas las imágenes que no estén en uso por algún contenedor, tengan etiqueta o no:

docker system prune -a

Es el comando que más espacio libera, pero tiene una consecuencia directa: la próxima vez que se necesite cualquiera de esas imágenes habrá que descargarla o construirla de nuevo. En una conexión lenta o con imágenes grandes, eso puede suponer varios minutos de espera.

Los volúmenes merecen una mención especial. docker system prune no los elimina a menos que se indique explícitamente con --volumes. Esta decisión de diseño es deliberada, porque los volúmenes suelen contener datos persistentes como bases de datos:

docker system prune -a --volumes

Esta combinación es la opción más agresiva disponible y conviene reservarla para situaciones en las que se tiene la certeza de que no hay datos valiosos en ningún volumen sin uso. En automatizaciones, la opción -f omite la confirmación interactiva; usarla junto con --volumes en un script sin revisar es una de las formas más rápidas de perder datos.

Limpiar por tipo de recurso

En lugar de una limpieza global, suele ser más prudente actuar sobre cada categoría por separado. Docker ofrece un subcomando prune para cada tipo de recurso.

Para las imágenes, docker image prune elimina únicamente las colgantes:

docker image prune

Con -a, elimina todas las imágenes que no utiliza ningún contenedor, no solo las colgantes. Es importante no confundir ambos comportamientos: la opción -a amplía la limpieza, no la restringe.

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] y
Deleted Images:
untagged: node:20-alpine
deleted: sha256:b19c4e5a7d03...
Total reclaimed space: 11.3GB

Para los contenedores detenidos:

docker container prune

Para las redes sin uso:

docker network prune

Y para los volúmenes:

docker volume prune

Aquí hay un cambio relevante respecto a versiones antiguas que muchos tutoriales no recogen. Desde Docker Engine 23, docker volume prune elimina por defecto solo los volúmenes anónimos sin uso, es decir, aquellos que Docker crea automáticamente sin nombre. Los volúmenes con nombre, como facturacion_pgdata, se conservan salvo que se añada la opción -a:

docker volume prune -a

Este comportamiento reduce el riesgo de borrar accidentalmente datos de bases de datos locales, pero también explica por qué, en sistemas actualizados, docker volume prune puede liberar menos espacio del esperado.

La caché de compilación de BuildKit

BuildKit es el motor de compilación predeterminado de Docker, y su caché suele ser el mayor consumidor de espacio en máquinas que construyen imágenes con frecuencia. Cada instrucción de un Dockerfile genera un resultado intermedio que se guarda para reutilizarlo en compilaciones futuras. Las montaduras de caché, como las que se usan para conservar dependencias de npm o pip entre compilaciones, también se almacenan aquí.

# syntax=docker/dockerfile:1
FROM python:3.14-slim
WORKDIR /app
COPY requirements.txt .
RUN --mount=type=cache,target=/root/.cache/pip pip install -r requirements.txt
COPY . .
CMD ["python", "-m", "facturacion"]

Para consultar el uso detallado de la caché:

docker buildx du

Para eliminar la caché que no está en uso:

docker builder prune

Y para eliminarla por completo, incluida la que podría reutilizarse:

docker builder prune -a

Una opción más equilibrada es eliminar solo la caché antigua mediante filtros. Por ejemplo, conservar la de los últimos tres días y borrar el resto:

docker builder prune --filter "until=72h" -f

Si se utilizan builders adicionales creados con docker buildx create, cada uno tiene su propia caché, que puede limpiarse indicando su nombre:

docker buildx ls
docker buildx prune --builder constructor-multiarq -f

Borrar la caché por completo es seguro en el sentido de que no se pierden datos, pero las siguientes compilaciones serán más lentas hasta que la caché se regenere. En servidores de CI conviene encontrar un equilibrio entre espacio y velocidad, en lugar de vaciarla en cada ejecución.

Eliminación selectiva con filtros

Los comandos prune aceptan filtros que permiten limpiar con mucha más precisión. Los dos más útiles son until, que actúa sobre recursos creados antes de un momento dado, y label, que actúa según las etiquetas de metadatos.

Eliminar imágenes sin uso con más de una semana de antigüedad:

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

Eliminar contenedores detenidos de más de 24 horas:

docker container prune --filter "until=24h"

Las etiquetas son especialmente potentes cuando se aplican de forma sistemática. Si las imágenes de un proyecto se construyen con una etiqueta que identifica su origen, luego pueden limpiarse sin afectar al resto:

docker build --label proyecto=facturacion --label entorno=pruebas -t api-facturacion:pr-482 .
docker image prune -a --filter "label=entorno=pruebas"

También existe la forma inversa, que elimina todo excepto lo que tiene una etiqueta concreta:

docker image prune -a --filter "label!=conservar=true"

Para ver qué recursos coinciden con un filtro antes de eliminarlos, conviene listar primero:

docker images --filter "label=entorno=pruebas"
docker ps -a --filter "status=exited" --filter "label=proyecto=facturacion"

Este hábito de listar antes de borrar es la mejor protección contra sorpresas, sobre todo cuando se combinan varios filtros.

Borrar imágenes y contenedores manualmente

A veces no interesa una limpieza basada en reglas, sino eliminar recursos concretos. Para borrar una imagen específica se usa docker image rm o su alias docker rmi:

docker image rm api-facturacion:pr-482

Si un contenedor, aunque esté detenido, usa la imagen, Docker se negará a eliminarla:

Error response from daemon: conflict: unable to remove repository reference "api-facturacion:pr-482" (must force) - container 9b1f3c2a7e60 is using its referenced image 3f9a1c7e2b40

La solución correcta es eliminar primero el contenedor y después la imagen. La opción -f fuerza el borrado, pero conviene entender qué contenedor la usa antes de recurrir a ella.

Para eliminar absolutamente todos los contenedores, incluidos los que están en ejecución, y después todas las imágenes, se pueden combinar los comandos de listado en modo silencioso:

docker rm -f $(docker ps -aq)
docker rmi -f $(docker images -aq)

En PowerShell la sintaxis es equivalente:

docker rm -f $(docker ps -aq)
docker rmi -f $(docker images -aq)

Estos comandos dejan el entorno local completamente vacío de contenedores e imágenes. Son útiles para empezar desde cero, pero detienen cualquier servicio en ejecución sin previo aviso. Si no hay contenedores, el primer comando mostrará un error indicando que requiere al menos un argumento, lo cual es inofensivo.

Cuando se trabaja con Docker Compose, la forma más limpia de eliminar los recursos de un proyecto es desde su propio directorio:

docker compose down --rmi local --volumes --remove-orphans

Esto detiene y elimina los contenedores del proyecto, las redes que creó, las imágenes construidas localmente para él y sus volúmenes. Es mucho más seguro que una limpieza global, porque solo afecta a ese proyecto.

Los logs de contenedores también ocupan

Una fuente de consumo que docker system df no muestra de forma separada son los logs. Con el controlador de logs predeterminado, json-file, cada contenedor escribe su salida estándar en un archivo que, sin configuración, crece sin límite. Un servicio que registra mucho puede generar gigabytes de logs en pocos días.

Para ver cuánto ocupa el log de un contenedor en Linux:

sudo du -h "$(docker inspect --format '{{.LogPath}}' api-facturacion)"
3.7G	/var/lib/docker/containers/9b1f3c2a7e60.../9b1f3c2a7e60...-json.log

La solución es limitar el tamaño en la configuración del demonio, en /etc/docker/daemon.json:

{
    "log-driver": "json-file",
    "log-opts": {
        "max-size": "20m",
        "max-file": "5"
    }
}

Tras modificar el archivo, se reinicia el servicio:

sudo systemctl restart docker

La configuración solo se aplica a los contenedores creados después del cambio; los existentes deben recrearse. Otra alternativa es el controlador local, que comprime los logs y aplica rotación por defecto. También se puede definir el límite por servicio en Docker Compose:

services:
    api:
        image: api-facturacion:latest
        logging:
            driver: json-file
            options:
                max-size: "20m"
                max-file: "3"

Docker Desktop y el disco virtual

En macOS y Windows, Docker Desktop ejecuta el motor dentro de una máquina virtual ligera, y todos los datos se guardan en un archivo de disco virtual. Esto añade una particularidad: liberar espacio dentro de Docker no siempre reduce de inmediato el tamaño de ese archivo en el sistema anfitrión.

En Windows con el backend de WSL 2, los datos se almacenan en un archivo de disco virtual que puede crecer considerablemente. Las versiones recientes de Docker Desktop recuperan espacio de forma más eficiente, pero si tras una limpieza el archivo sigue ocupando mucho, se puede apagar WSL y compactar el disco con las herramientas del sistema:

wsl --shutdown

En la interfaz de Docker Desktop, la sección de configuración de recursos permite consultar y ajustar el límite del disco virtual, y el panel de solución de problemas incluye opciones para limpiar o restablecer todos los datos. Estas opciones eliminan absolutamente todo, incluidos los volúmenes, por lo que solo deben usarse como último recurso.

Automatizar la limpieza de forma segura

En servidores de integración continua y máquinas de compilación, la limpieza manual no es sostenible. Lo habitual es programarla. El enfoque más sencillo en Linux es un temporizador de systemd o una tarea de cron que ejecute una limpieza moderada con filtros de antigüedad.

Un script prudente podría ser este:

#!/usr/bin/env bash
# /usr/local/bin/limpieza-docker.sh
set -euo pipefail

echo "Antes de la limpieza:"
docker system df

docker container prune -f --filter "until=24h"
docker image prune -a -f --filter "until=168h" --filter "label!=conservar=true"
docker builder prune -f --filter "until=72h"
docker network prune -f --filter "until=24h"

echo "Después de la limpieza:"
docker system df
sudo chmod +x /usr/local/bin/limpieza-docker.sh

Y una entrada de cron para ejecutarlo todos los días a las tres de la madrugada:

0 3 * * * /usr/local/bin/limpieza-docker.sh >> /var/log/limpieza-docker.log 2>&1

Este script no toca los volúmenes a propósito. Una política de limpieza programada debe ser conservadora con los datos persistentes y dejar su eliminación en manos de una decisión explícita.

En pipelines de CI con ejecutores efímeros el problema desaparece, porque cada trabajo se ejecuta en una máquina nueva. En cambio, con ejecutores persistentes, como los runners autoalojados de GitHub Actions o GitLab, conviene añadir un paso de limpieza al final del pipeline o una tarea programada en el propio servidor. Hay que tener en cuenta que borrar todas las imágenes en cada ejecución obliga a descargarlas de nuevo y ralentiza los pipelines; los filtros por antigüedad suelen ofrecer mejor equilibrio.

Reducir el problema desde el origen

La limpieza resuelve los síntomas, pero también se puede reducir el volumen de datos que se generan. Construir imágenes más pequeñas significa menos espacio en disco, descargas más rápidas y menos superficie de ataque.

Las compilaciones multietapa permiten separar el entorno de compilación del de ejecución, de modo que la imagen final solo contiene lo necesario:

# syntax=docker/dockerfile:1
FROM node:22 AS compilacion
WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .
RUN npm run build

FROM node:22-slim
WORKDIR /app
COPY --from=compilacion /app/dist ./dist
COPY --from=compilacion /app/node_modules ./node_modules
CMD ["node", "dist/servidor.js"]

Un archivo .dockerignore evita enviar al contexto de compilación archivos innecesarios que, además de ralentizar el proceso, pueden acabar dentro de la imagen:

node_modules
.git
dist
*.log
.env
coverage

Usar imágenes base ligeras, variantes slim o alpine cuando la compatibilidad lo permite, y agrupar instrucciones relacionadas en una sola capa también reduce el tamaño final. Adoptar imágenes base más ligeras tiene un efecto acumulado considerable en equipos que construyen cientos de imágenes al mes.

Otra práctica útil es evitar reutilizar la misma etiqueta para versiones distintas en entornos de desarrollo, o al menos ser consciente de que cada reconstrucción con la etiqueta latest deja una imagen colgante que tarde o temprano habrá que eliminar.

Herramientas complementarias y alternativas

Los comandos nativos de Docker cubren la mayoría de las necesidades, pero algunas herramientas externas ayudan a entender mejor el consumo. Dive es una utilidad de código abierto que permite explorar una imagen capa por capa, ver qué archivos añade cada instrucción del Dockerfile y detectar espacio desperdiciado, como archivos que se copian en una capa y se eliminan en otra:

dive api-facturacion:latest

Para el análisis del sistema de archivos del anfitrión, utilidades como ncdu o du permiten recorrer el directorio de datos de Docker y localizar qué subdirectorios crecen más, algo especialmente útil cuando el problema son los logs o volúmenes concretos:

sudo ncdu /var/lib/docker

Quien trabaja con Podman como alternativa a Docker encontrará una experiencia casi idéntica, porque su interfaz de línea de comandos reproduce la de Docker. Los equivalentes son directos:

podman system df
podman system prune -a
podman image prune

La diferencia más relevante es que Podman suele ejecutarse sin demonio y en modo rootless, de modo que cada usuario tiene su propio almacenamiento en su directorio personal. Al diagnosticar el espacio ocupado en una máquina compartida hay que revisar el consumo de cada usuario por separado, y no solo el del almacenamiento global del sistema.

Errores comunes y cómo evitarlos

El error más grave es ejecutar docker system prune -a --volumes -f sin revisar qué volúmenes existen. Un volumen sin contenedor asociado puede contener la base de datos local de un proyecto que simplemente estaba detenido. Antes de borrar volúmenes conviene listarlos:

docker volume ls --filter dangling=true

Si hay dudas sobre un volumen, se puede hacer una copia de seguridad rápida montándolo en un contenedor temporal:

docker run --rm -v prototipo_viejo_mongo:/datos -v "$PWD":/respaldo alpine \
  tar czf /respaldo/prototipo_viejo_mongo.tar.gz -C /datos .

Otro error frecuente es esperar que docker image prune libere mucho espacio cuando en realidad la mayor parte del consumo está en la caché de BuildKit. Revisar docker system df antes de limpiar evita actuar sobre la categoría equivocada.

También es habitual olvidar los logs. Un disco lleno con pocas imágenes y contenedores casi siempre apunta a logs sin rotación o a volúmenes de bases de datos que han crecido.

Por último, en servidores de producción nunca debe ejecutarse una limpieza agresiva sin entender qué servicios dependen de cada imagen. Si un contenedor se reinicia y su imagen ya no está disponible localmente ni en el registro, el servicio no podrá arrancar.

Solución de problemas frecuentes

Si Docker no puede escribir por falta de espacio, los mensajes típicos son similares a este:

failed to register layer: write /var/lib/docker/...: no space left on device

En ese caso, el orden recomendado es revisar el consumo con docker system df, eliminar la caché de compilación, que es segura, y después las imágenes y contenedores sin uso. Si el espacio sigue sin aparecer, conviene revisar los logs y los volúmenes.

Si un comando de limpieza no libera espacio en el sistema anfitrión, puede deberse a que otro proceso mantiene abiertos los archivos o, en Docker Desktop, a que el disco virtual no se ha compactado. Reiniciar el servicio de Docker suele resolver el primer caso.

Cuando aparece el error de que una imagen está referenciada en varios repositorios, significa que tiene varias etiquetas apuntando al mismo identificador:

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

Se puede eliminar cada etiqueta por separado con docker image rm nombre:etiqueta o forzar el borrado por identificador con -f.

Si se quiere mover todo el almacenamiento de Docker a otro disco con más capacidad, se puede configurar la ruta en daemon.json con la clave data-root, detener el servicio, copiar el contenido del directorio actual y reiniciar:

{
    "data-root": "/mnt/datos/docker"
}

Conclusiones

Docker acumula imágenes, contenedores, volúmenes, caché de compilación y logs de forma silenciosa, y en poco tiempo puede ocupar una parte considerable del disco. El primer paso para resolverlo siempre es medir con docker system df y su versión detallada, porque el origen del consumo no suele ser el que se espera.

A partir de ahí, la estrategia depende del contexto. docker system prune ofrece una limpieza segura del día a día, la opción -a libera mucho más a cambio de volver a descargar imágenes, y los comandos específicos por tipo de recurso, combinados con filtros de antigüedad y etiquetas, permiten un control mucho más fino. Los volúmenes requieren siempre una decisión explícita, y desde Docker Engine 23 los volúmenes con nombre solo se eliminan con docker volume prune -a.

La caché de BuildKit y los logs sin rotación son los dos consumidores más infravalorados; controlarlos con docker builder prune y límites en daemon.json evita la mayoría de los problemas de espacio. Combinar una limpieza programada y conservadora con imágenes más pequeñas, compilaciones multietapa y un buen .dockerignore mantiene el entorno sano sin sacrificar la velocidad de trabajo ni poner en riesgo los datos.