759 - He liberado 600 GB de Docker! El comando que NO conoces
Aprende a liberar más de 600 GB en Docker con comandos clave de limpieza y automatización. ¡Optimiza tu servidor Linux y no te quedes sin espacio!
En ocasiones, en muchas ocasiones, nos obsesionamos en comprar almacenamiento, ya sea en el VPS que tenemos contratado, o en el NAS que tenemos en casa, o en el disco duro de nuestro equipo. Y la solución no es comprar más almacenamiento, sino liberar el que ya tenemos. La realidad es que tenemos un inquilino en nuestro sistema que es un auténtico acumulador de basura. Y ese inquilino no es ni mas ni menos que Docker
Hoy te traigo un caso real, mi propio servidor, donde he descubierto que, entre el build cache, volúmenes huérfanos, imágenes obsoletas y contenedores parados, tenía más de 600 GB ocupados por Docker. Y lo peor de todo es que no tenía ni idea de ello.
En este episodio, vamos a pasar la motosierra a Docker. Vamos a ver como diagnosticar el problema, como utilizar herramientas como fd para localizar los archivos mas pesados que están ocupando espacio, y lo mas importante, como automatizar la limpieza para que este 2026 no vuelvas a recibir un alerta de disco lleno nunca mas.

He liberado 600 GB de Docker! El comando que NO conoces
Una mirada en profundidad
Seguro que te ha pasado, ya mas de una vez, que haces un df -h o mejor un dust y te encuentras con que tu disco está casi lleno. Y lo peor de todo es que no tienes ni idea de qué es lo que está ocupando tanto espacio.
En tu caso, es posible que ese directorio que está ocupando tanto espacio sea /var/lib/docker. En mi caso particular es /data/docker porque en mi caso, hace algún tiempo decidí tener el almacenamiento de docker en otra unidad.
Si este es el caso, es muy posible que puedas liberar una gran cantidad de espacio eliminando datos innecesarios de Docker. Y aquí es donde interviene el comando estrella de este episodio, que no es ni mas ni menos que docker system df.
El comando docker system df es una herramienta fundamental para gestionar el almacenamiento en Docker. Funciona de manera similar al comando df de Linux, pero enfocado específicamente en los recursos que gestiona el motor de Docker. Así sobre el resultado que nos proporciona esta herramienta son los siguientes,
- TYPE: El tipo de recurso de Docker (Imágenes, Contenedores, Volúmenes o Caché).
- TOTAL: El número total de objetos que tienes en esa categoría.
- ACTIVE: Cuántos de esos objetos están siendo usados por un contenedor que está en ejecución actualmente.
- SIZE: El espacio real que ocupan en el disco.
- RECLAIMABLE: El espacio que recuperarías inmediatamente si ejecutaras un comando de limpieza (
prune). Indica qué parte del tamaño total pertenece a objetos que no están activos.
Dentro de TPYE, tenemos cuatro categorías principales,
- Imágenes
- Contenedores
- Contenedores
- Volúmenes locales
En mi caso particular, cuando he iniciado el diagnóstico, el resultado de docker system df ha sido el siguiente,
| TYPE | TOTAL | ACTIVE | SIZE | RECLAIMABLE |
|---|---|---|---|---|
| Images | 53 | 1 | 9.032GB | 1.076GB (11%) |
| Containers | 1 | 1 | 63B | 0B (0%) |
| Local Volumes | 26 | 1 | 19.81GB | 19.72GB (99%) |
| Build Cache | 1507 | 0 | 634.5GB | 632.3GB |
Esta es la situación en la que me encontraba incialmente. Como puedes ver, tenía 634.5 GB ocupados en el build cache y casi 20 GB en volúmenes huérfanos.
El build cache y su peligro
Pero ¿Que es el build cache?. Son las capas intermedias que Docker guarda cada vez que haces un *docker build. Básicamente se trata de facilitar el trabajo de construir una imagen, de forma que la próxima vez que tengas que construir la misma imagen, Docker pueda reutilizar las capas que ya tiene en el caché, en lugar de tener que reconstruir todo desde cero. Esto acelera significativamente el proceso de construcción de imágenes.
Realmente tiene sentido que en mi caso ,tenga un tamaño grande, porque como te vengo taladrando en los últimos episodios, estoy desarrollando en Rust, y cada vez que hago un cambio en el código, tengo que reconstruir la imagen. Y claro, cada vez que hago esto, Docker guarda las capas intermedias en el build cache, lo que con el tiempo ha ido acumulando una gran cantidad de datos.
Sin embargo, 630 GB es una barbaridad*. Podrías pensar en borrarlo todo, pero, claro, la próxima vez que tengas que construir una imagen, el proceso será mucho más lento porque Docker tendrá que reconstruir todo desde cero. Así que tenemos que recurrir a una solución de compromiso, que podría ser borrarlo todo, excepto lo que hayas utilizado en los últimos 3 días.tengo que ver cómo hacerlo.
Si lo quieres borrar todo, utilizas,
docker builder prune -a
El flag -a es el que marca la diferencia, porque si no lo pones, solo eliminará las capas que no están asociadas a ninguna imagen.
Pero si quieres conservar lo que has utilizado en los últimos 3 días, puedes utilizar el siguiente comando,
docker builder prune -a --filter "until=72h"
Esto es lo que mas sentido tiene, sobre todo, si estás en mi caso, que en las últimas semanas estoy haciendo decentas de imágenes.
Volúmenes huérfanos
Un volumen huérfano es un volumen de Docker que ya no está asociado a ningún contenedor. Esto puede ocurrir cuando eliminas un contenedor sin eliminar sus volúmenes asociados. Los volúmenes huérfanos pueden ocupar espacio en disco innecesariamente, especialmente si contienen datos grandes. Esto es como cuando desinstalas una aplicación en tu sistema operativo, pero los archivos de configuración y datos permanecen en el disco. El programa ya no está allí, pero los datos siguen ocupando espacio.
¿Como aparecen los volúmenes huérfanos? Muy sencillo, cuando eliminas un contenedor con docker rm contenedor, los volúmenes asociados a ese contenedor no se eliminan automáticamente. Esto es intencionado, ya que Docker asume que podrías querer conservar los datos almacenados en esos volúmenes para futuros usos.
Los logs…
Otro de los grandes problemas con el que podemos encontrarnos es con los logs de los contenedores. En ocasiones, los contenedores pueden generar una gran cantidad de datos de registro (logs), especialmente si están configurados para un nivel de detalle alto. Estos logs se almacenan en el sistema de archivos del host y pueden crecer rápidamente, ocupando espacio significativo en disco.
La cuestión es buscar esos logs. Por supuesto que lo puedes hacer con find, pero mi recomendación es que utilices su alternativa que es mas rápida y quirúrgicamente mucho mas precisa. Lo puedes hacer con,
sudo fd -e log -S +100M /data/docker
Podríamos utilizar un comando similar, no solo para localizar archivos de log grandes, sino también para borrarlos si es necesario. Por ejemplo, para eliminar todos los archivos de log mayores de 100 MB, podrías usar:
sudo fd -e log -S +100M /data/docker -x rm {}
Sin embargo, la solución no es borrarlos cada cierto tiempo, sino configurar los contenedores para que roten los logs automáticamente. Esto se puede hacer utilizando las opciones de configuración de logging de Docker.
Para ello, simplemente tienes que modificar el archivo /etc/docker/daemon.json y añadir lo siguiente
{
"log-driver": "json-file",
"log-opts": {
"max-size": "10m",
"max-file": "3"
},
}
Automatización total
La cuestión es que si quieres evitarte sorpresas, o mejor si quieres mantener tu pisito limpio, tienes que automatizar la limpieza de Docker. Para ello, lo mejor es crear un script que haga todo el trabajo por ti y programarlo para que se ejecute periódicamente.
El script puede tener una pinta como la siguiente, /home/${USER}/.local/bin/docker-clean
#!/bin/bash
# Ruta de logs (ajustada a tu data-root si fuera necesario)
LOG_FILE="/var/log/docker-cleanup.log"
echo "--- Iniciando Limpieza Docker: $(date) ---" | tee -a $LOG_FILE
# 1. Espacio antes de la limpieza
BEFORE=$(docker system df | awk '/Total/ {print $0}' | head -n 1)
echo "Estado inicial: $BEFORE" | tee -a $LOG_FILE
# 2. Limpieza de Contenedores parados (más de 24h)
echo "Limpiando contenedores antiguos..."
docker container prune -f --filter "until=24h" | tee -a $LOG_FILE
# 3. Limpieza de Imágenes sin etiqueta (dangling)
echo "Limpiando imágenes huérfanas..."
docker image prune -a -f | tee -a $LOG_FILE
# 4. Limpieza de Volúmenes huérfanos (El cementerio de datos)
echo "Limpiando volúmenes en desuso..."
docker volume prune -f | tee -a $LOG_FILE
# 5. Limpieza del Build Cache (El monstruo de los GB)
# Solo borra lo que tenga más de 48h para no afectar al desarrollo activo
echo "Limpiando caché de construcción..."
docker builder prune -a -f --filter "until=48h" | tee -a $LOG_FILE
# 6. Espacio después de la limpieza
AFTER=$(docker system df | awk '/Total/ {print $0}' | head -n 1)
echo "Estado final: $AFTER" | tee -a $LOG_FILE
echo "--- Limpieza completada con éxito ---" | tee -a $LOG_FILE
Y para que se ejecuta de forma completamente programada, puedes añadir una entrada en el crontab de tu sistema. Por ejemplo, para ejecutarlo todos los domingos a las 8 de la mañana, puedes crear el siguiente timer, en ~/.config/systemd/user/docker-clean.timer,
[Unit]
Description=Limpieza semanal de Docker
[Timer]
OnCalendar=Sun *-*-* 08:00:00
Persistent=true
[Install]
WantedBy=timers.target
Y el servicio correspondiente, en ~/.config/systemd/user/docker-clean.service,
[Unit]
Description=Servicio de limpieza Docker de Atareao
[Service]
Type=oneshot
ExecStart=/bin/bash /home/${USER}/.local/bin/docker-clean
Indicar que tendrás que crear el archivo de log /var/log/docker-cleanup.log y darle permisos de escritura al usuario que ejecuta el servicio.
Conclusión
Con estas modificaciones el final queda como sigue,
| Elemento | Antes | Después (Volúmenes) | Potencial (Builder) |
|---|---|---|---|
| Volúmenes | 19.81 GB | 93.7 MB | 93.7 MB |
| Build Cache | 634.5 GB | 246.1 GB | ~1.4 GB |
| Estado Disco | Agonizando | Respirando | ¡Como nuevo! |
Como ves el cambio es significativo, sobre todo en el build cache, que ha pasado de 634.5 GB a 246.1 GB, y lo mejor de todo es que podría reducirse aún más, hasta aproximadamente 1.4 GB. Claro que esto sería a costa de no tener ese caché para futuras construcciones. Así, que por el momento se queda así.