Configurar BuildKit garbage collection y systemd timers para limpieza automática de imágenes y volúmenes Docker en VPS: tutorial práctico

Docker & Containers · Por · Publicado el · 9 min de lectura

Fragmento de código HTML/CSS en editor en el monitor, sintaxis coloreada y enfoque selectivo
Editor mostrando código HTML/CSS, transmite un tema técnico y profesional Foto: Bibek ghosh / Pexels

Este tutorial muestra cómo configurar BuildKit garbage collection y systemd timers para la limpieza automática de imágenes, caché de build y volúmenes Docker en una VPS. Aplicarás políticas conservadoras, usarás labels para excepciones y crearás servicios/timers de systemd para controlar cache de build, imágenes antiguas y volúmenes temporales, evitando sorpresas en producción.

Requisitos y qué se automatiza

La rutina combina el GC de BuildKit (caché de build) con la eliminación controlada de artefactos Docker (containers detenidos, imágenes antiguas y volúmenes etiquetados). El objetivo es reducir el uso de disco sin afectar los servicios en ejecución.

Antes de continuar, confirma que tienes acceso sudo, Docker y systemd activos. Haz un snapshot/backup si hay datos críticos. Algunos pasos eliminan datos antiguos o temporales.

Por seguridad, las imágenes activas no se eliminan y los volúmenes solo se borran cuando están etiquetados con una label de autoclean y no están en uso. Si vas a preparar el host, consulta cómo instalar Docker y Docker Compose en Linux en esta guía.

  • Docker Engine y buildx instalados
  • Acceso sudo para crear services/timers
  • Espacio libre temporal para ejecutar la limpieza
  • Backups o snapshot del host
Objetivo Comando principal Criterios Observaciones
Caché de build (BuildKit) docker buildx prune -af --keep-storage 20GB Mantener N GB de caché No toca imágenes/volúmenes; solo caché de build
Imágenes docker image prune -a -f --filter 'until=240h' Edad > 10 días y no usadas Puede eliminar versiones antiguas no referenciadas
Containers parados docker container prune -f --filter 'until=240h' Edad > 10 días y parados Libera capas y logs antiguos
Volúmenes docker volume rm $(docker volume ls -q --filter 'label=autoclean=true' --filter 'dangling=true') Solo con label autoclean y sin uso Evita borrar datos importantes por error

Qué limpia realmente BuildKit con GC

El GC de BuildKit elimina únicamente caché de build (capas y artefactos temporales generados en los builds). No borra imágenes publicadas, containers, volúmenes ni redes. Usaremos docker buildx prune con un límite de almacenamiento para mantener un caché útil y estable.

Para imágenes y volúmenes crearemos rutinas separadas con filtros de edad y labels. Así, cada objetivo tiene reglas claras e independientes.

Cómo ver el consumo de caché, imágenes y volúmenes ahora

Antes de automatizar, mide dónde está el consumo. buildx du muestra el uso del caché de build por repositorio/etapa. docker system df detalla imágenes, containers y volúmenes. Lista los volúmenes dangling para identificar candidatos a limpieza sin riesgo.

# Uso do cache de build (BuildKit)
docker buildx du --verbose

# Uso geral de disco (imagens, containers, volumes)
docker system df -v

# Volumes sem uso (dangling)
docker volume ls --filter dangling=true

Configurar BuildKit GC con systemd timers (política por almacenamiento)

Vamos a programar docker buildx prune para mantener el caché de build dentro de un límite (--keep-storage). Ejecutarlo mediante systemd garantiza logs y reanudación si el host estuvo apagado en el horario programado.

Si usas un builder dedicado (driver docker-container), ajusta el objetivo del builder con la flag correspondiente a tu entorno. Buenas prácticas para timers están en la guía para migrar cron a systemd timers. Para acelerar builds con caché remoto, consulta el tutorial de cache remoto con BuildKit.

# Service e timer para GC do BuildKit (cache de build)
# Crie os arquivos abaixo

sudo tee /etc/systemd/system/buildkit-prune.service >/dev/null <<'EOF'
[Unit]
Description=BuildKit cache prune (docker buildx)
After=docker.service
Requires=docker.service

[Service]
Type=oneshot
# Ajuste o limite conforme seu disco (ex.: 10GB, 20GB, 50GB)
ExecStart=/usr/bin/docker buildx prune -af --keep-storage 20GB
Nice=10
IOSchedulingClass=idle
EOF

sudo tee /etc/systemd/system/buildkit-prune.timer >/dev/null <<'EOF'
[Unit]
Description=Run BuildKit cache prune weekly

[Timer]
OnCalendar=Sun *-*-* 03:15:00
RandomizedDelaySec=30m
Persistent=true

[Install]
WantedBy=timers.target
EOF

sudo systemctl daemon-reload
sudo systemctl enable --now buildkit-prune.timer

Script seguro para limpiar imágenes y volúmenes por edad y labels

El script siguiente elimina containers detenidos antiguos, limpia imágenes no usadas y borra únicamente volúmenes sin uso etiquetados con autoclean=true. Revisa los filtros antes de activar en producción. Para proteger imágenes críticas, usa la label keep=true y el filtro del prune las preservará.

# Dica: rotule recursos para controle fino (exemplos):
# docker volume create --label autoclean=true tmp_cache
# docker build -t minhaimg:latest --label keep=true .

sudo tee /usr/local/sbin/docker-gc-safe.sh >/dev/null <<'EOF'
#!/usr/bin/env bash
set -euo pipefail
log() { echo "[$(date -Is)] $*"; }

if ! docker info >/dev/null 2>&1; then
  log "Docker não está acessível; saindo."
  exit 0
fi

# 1) Containers parados antigos (ex.: >10 dias)
log "Pruning containers parados (>10d)"
docker container prune -f --filter "until=240h" || true

# 2) Imagens não usadas antigas, preservando as rotuladas com keep
log "Pruning imagens antigas não usadas (>10d), exceto label keep"
docker image prune -a -f \
  --filter "until=240h" \
  --filter "label!=keep" || true

# 3) Volumes sem uso e rotulados para autoclean
log "Removendo volumes sem uso com label autoclean=true"
mapfile -t VOLS < <(docker volume ls -q \
  --filter "label=autoclean=true" \
  --filter "dangling=true") || true
if ((${#VOLS[@]} > 0)); then
  docker volume rm "${VOLS[@]}" || true
else
  log "Nenhum volume elegível encontrado"
fi

log "Limpeza concluída"
EOF

sudo chmod +x /usr/local/sbin/docker-gc-safe.sh

Cómo programar el script de limpieza con un systemd timer

Crea un service oneshot y un timer diario con un delay aleatorio para evitar colisiones con ventanas de backup. Persistent=true hace que la tarea pendiente se ejecute cuando el host vuelva a arrancar.

Si usas Docker rootless, crea las units en el systemd de usuario (systemctl --user) y coloca el script en tu $HOME/bin. Para integrar con tu estrategia de protección, considera programar snapshots y uploads a object storage con timers, como en el tutorial de LVM+Restic en S3 paso a paso.

sudo tee /etc/systemd/system/docker-gc.service >/dev/null <<'EOF'
[Unit]
Description=Rotina de limpeza segura de Docker (containers, imagens e volumes com label)
After=docker.service
Requires=docker.service

[Service]
Type=oneshot
ExecStart=/usr/local/sbin/docker-gc-safe.sh
Nice=10
IOSchedulingClass=idle
EOF

sudo tee /etc/systemd/system/docker-gc.timer >/dev/null <<'EOF'
[Unit]
Description=Agendar limpeza segura de Docker diariamente

[Timer]
OnCalendar=daily
RandomizedDelaySec=45m
Persistent=true

[Install]
WantedBy=timers.target
EOF

sudo systemctl daemon-reload
sudo systemctl enable --now docker-gc.timer

Cómo confirmar que funcionó y dónde ver logs

Confirma timers activos, consulta los logs y mide la reducción de espacio con docker system df y buildx du. Si una ejecución se perdió, Persistent=true reprogramará la ejecución al arrancar el sistema. Esto ayuda a mantener BuildKit GC y systemd timers fiables en tu VPS.

# Timers ativos
systemctl list-timers '*buildkit*' '*docker-gc*'

# Logs da última execução
journalctl -u buildkit-prune.service -u docker-gc.service --since "-2d" -e

# Medir economia de espaço
docker system df -v
docker buildx du --verbose

Problemas comunes y ajustes finos

Algunos escenarios requieren ajustar los filtros. Prioriza etiquetas para excepciones y revisa las ventanas de edad en entornos con baja tasa de despliegue. En registros privados extensos, ejecuta limpieza de imágenes semanalmente y caché diariamente.

  • Imágenes críticas desaparecen: añade label keep=true al build y preserva con --filter label!=keep.
  • Caché de build insuficiente: aumenta --keep-storage (ej.: 30GB).
  • Volúmenes borrados antes de tiempo: etiqueta autoclean=true solo en volúmenes temporales; nunca en volúmenes de bases de datos.
  • Docker rootless: crea timers con systemctl --user y asegúrate de que la sesión de usuario esté habilitada.
  • Colisión con backups: ajusta OnCalendar fuera de la ventana de snapshot/backup.
  • Sin espacio para ejecutar prune: limpia primero solo dangling (docker image prune -f) y, si hace falta, mueve volúmenes grandes a otro disco siguiendo el tutorial de migración de volúmenes Docker paso a paso.

Preguntas frecuentes

¿Es seguro ejecutar prune automáticamente en Docker?

Sí, cuando defines filtros conservadores y usas labels para excepciones. Aplica edad (until=...) para containers e imágenes y borra solo volúmenes sin uso con label autoclean=true. Ten snapshots o backups recientes antes de la primera ejecución.

¿Cuál es la diferencia entre docker system prune y buildx prune?

docker system prune elimina recursos de Docker (containers detenidos, imágenes no usadas, redes y volúmenes anónimos). docker buildx prune elimina únicamente el caché de build de BuildKit. Combínalos con criterios distintos y agendamientos separados.

¿Con qué frecuencia debo ejecutar el GC y los timers?

Diariamente para caché de build y containers detenidos, y semanalmente para imágenes antiguas suele equilibrar espacio y rendimiento. Ajusta la ventana de edad y el keep-storage según tu tasa de despliegue y el tamaño del disco.

¿Cómo evitar que imágenes críticas sean eliminadas?

Etiquétalas con keep=true en el build y añade el filtro --filter label!=keep al docker image prune. Las versiones marcadas se preservarán aunque sean antiguas y no estén referenciadas por containers.

¿Puedo usar esto en Docker rootless?

Sí. Crea el service y el timer en el systemd de usuario (systemctl --user) y asegúrate de que la sesión de usuario esté habilitada al iniciar sesión. El resto del flujo (buildx prune, image prune, volume rm) funciona de la misma forma.

¿El timer se ejecuta si la VPS está apagada en el horario?

Sí. Cuando el timer tiene Persistent=true, systemd dispara la ejecución pendiente tan pronto el sistema vuelva a arrancar. Los logs quedan en journalctl para auditoría y troubleshooting.

¿Quieres estandarizar la limpieza sin riesgos?

¿Necesitas revisar filtros, labels y horarios para tu entorno? Describe tu escenario y validamos juntos las políticas, con dry‑run y rollback seguro.

Hablar sobre políticas de GC

Ver también