Cómo usar BuildKit y caché remota para acelerar builds Docker en pipelines CI
Docker & Containers · Por Kevin Marcondes · Publicado el · Actualizado el · 9 min de lectura
Esta guía muestra cómo usar BuildKit y caché remota para acelerar builds Docker en pipelines CI. Habilitarás Buildx, prepararás el Dockerfile para aprovechar cachés de dependencias y configurarás caché remota en un registry desde el CI. El objetivo es reducir el tiempo de ejecución manteniendo reproducibilidad y seguridad.
Requisitos previos y visión general de BuildKit en CI
BuildKit es el motor de build de Docker con paralelismo, control fino del caché y funciones como mounts de caché y secretos. En runners efímeros de CI, el caché local se pierde; por eso usamos caché remota para reutilizar capas entre ejecuciones.
Necesitas Docker reciente con Buildx. Si hace falta, consulta cómo instalar Docker y Compose en Linux en Instalar Docker y Docker Compose. Si usas multistage, este artículo complementa Imágenes Docker ligeras con multistage.
La caché remota vía registry almacena metadatos y capas como artefactos OCI. Funciona en cualquier CI que pueda autenticarse y hacer push/pull en un registry. No almacenes secretos en el caché; usa mounts de secreto de BuildKit cuando los necesites.
- Cuenta en un registry con permisos de push/pull.
- Runner de CI con Docker disponible (con o sin DinD).
- Dockerfile compatible con BuildKit y, preferiblemente, multistage.
- Acceso de red del CI al registry de imágenes.
1) Habilitar BuildKit y Buildx localmente
Activa BuildKit y Buildx para probar el flujo localmente antes de llevarlo al CI. Crea un builder dedicado e inicializa el driver; así verás los mismos logs y comportamiento que en el pipeline.
Después, ejecuta un build simple y confirma que Buildx está en uso. Usa el modo plain para ver los hits de caché claramente en los logs.
# Ativar BuildKit na sessão atual
export DOCKER_BUILDKIT=1
export COMPOSE_DOCKER_CLI_BUILD=1
docker buildx version
# Criar e usar um builder baseado em container
docker buildx create --name ci-builder --use
# Inicializar capacidades (QEMU, cache driver, etc.)
docker buildx inspect --bootstrap
# Build de teste com saída detalhada
docker buildx build --progress=plain -t test-image .
2) Preparar el Dockerfile para aprovechar el caché (cache mounts)
Para aprovechar la caché remota, reduce las invalidaciones y usa mounts de caché en las etapas que descargan dependencias. Con BuildKit, RUN acepta --mount=type=cache para persistir directorios como caché entre builds.
Incluye la directiva de sintaxis del Dockerfile para versiones recientes y aísla dependencias en stages. Evita copiar el repositorio entero antes del install. Nunca escribas secretos en capas; usa secret mounts cuando haga falta.
- Usa COPY selectivo de los manifests antes del install.
- Separa dependencias de build y de producción.
- Evita invalidaciones colocando primero los archivos que cambian poco.
- Activa --mount=type=cache para gestores de paquetes.
# syntax=docker/dockerfile:1.6
FROM node:20-slim AS base
WORKDIR /app
# 1) Dependências para build (usa cache do npm)
FROM base AS deps
COPY package*.json ./
RUN --mount=type=cache,target=/root/.npm \
npm ci
# 2) Build do app
FROM base AS build
COPY --from=deps /app/node_modules /app/node_modules
COPY . .
RUN --mount=type=cache,target=/root/.npm \
npm run build
# 3) Dependências de produção (sem dev)
FROM base AS prod-deps
COPY package*.json ./
RUN --mount=type=cache,target=/root/.npm \
npm ci --omit=dev
# 4) Runtime enxuto
FROM node:20-slim AS runtime
WORKDIR /app
ENV NODE_ENV=production
COPY --from=prod-deps /app/node_modules ./node_modules
COPY --from=build /app/dist ./dist
CMD ["node", "dist/index.js"]
3) Configurar caché remota vía registry con Buildx
En runners efímeros, usa caché remota basada en registry. Buildx puede exportar e importar caché con las flags --cache-to y --cache-from. La referencia apunta a una etiqueta reservada del caché.
Autentica en el registry antes del build. Usa mode=max para escribir la máxima cantidad de metadatos posible y mejorar los hits. Asegura que la etiqueta del caché sea estable para que todos los builds la reutilicen.
# Variáveis de exemplo (ajuste para seu registry)
export REGISTRY=registry.example.com
export IMAGE_NAME=myapp
export IMAGE_TAG=sha-$(git rev-parse --short HEAD)
export IMAGE=$REGISTRY/$IMAGE_NAME:$IMAGE_TAG
export CACHE_REF=$REGISTRY/$IMAGE_NAME:buildcache
# Login no registry
echo "$REG_PASS" | docker login "$REGISTRY" -u "$REG_USER" --password-stdin
# Build e push com cache remoto
docker buildx build \
--builder ci-builder \
--progress=plain \
--cache-from=type=registry,ref=$CACHE_REF \
--cache-to=type=registry,ref=$CACHE_REF,mode=max \
-t $IMAGE \
--push \
.
4) Ejemplo en GitHub Actions con caché en el registry
En GitHub Actions, usa acciones para configurar Buildx, autenticarse en el registry y ejecutar el build con cache-from/to. La etiqueta del caché se mantiene fija y la de la imagen puede usar el SHA del commit.
Si aún no tienes un pipeline base, consulta Configurar pipeline de despliegue automático y, para ejemplos en PHP, CI/CD con GitHub Actions: guía PHP/Laravel.
name: docker-build
on:
push:
branches: [ main ]
jobs:
build:
runs-on: ubuntu-latest
permissions:
contents: read
packages: write
steps:
- uses: actions/checkout@v4
- uses: docker/setup-buildx-action@v3
- name: Login registry
uses: docker/login-action@v3
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Build and push with registry cache
uses: docker/build-push-action@v5
with:
context: .
push: true
tags: ghcr.io/${{ github.repository }}:sha-${{ github.sha }}
cache-from: type=registry,ref=ghcr.io/${{ github.repository }}:buildcache
cache-to: type=registry,ref=ghcr.io/${{ github.repository }}:buildcache,mode=max
5) Ejemplo en GitLab CI (o CI con Docker-in-Docker)
En pipelines con Docker-in-Docker, habilita el servicio dind, desactiva TLS en el runner cuando sea apropiado y usa Buildx para exportar e importar la caché en el registry del proyecto.
La autenticación usa variables seguras del CI. Ten cuidado al exponer logs: no imprimas tokens. Mantén la referencia de caché estable para maximizar la reutilización entre ramas.
stages: [build]
variables:
DOCKER_TLS_CERTDIR: ""
DOCKER_HOST: tcp://docker:2375
DOCKER_DRIVER: overlay2
services:
- name: docker:24.0-dind
build-image:
image: docker:24.0
stage: build
script:
- apk add --no-cache curl
- echo "$CI_REGISTRY_PASSWORD" | docker login "$CI_REGISTRY" -u "$CI_REGISTRY_USER" --password-stdin
- docker buildx create --use --name ci-builder
- docker buildx build --progress=plain \
--cache-from=type=registry,ref=$CI_REGISTRY_IMAGE:buildcache \
--cache-to=type=registry,ref=$CI_REGISTRY_IMAGE:buildcache,mode=max \
-t $CI_REGISTRY_IMAGE:$CI_COMMIT_SHORT_SHA \
--push \
.
only:
- main
6) Cómo confirmar que la caché remota está funcionando
Ejecuta un build para calentar la caché y luego repite cambiando solo archivos no críticos. Debes ver "CACHED" o etapas saltadas rápidamente en los logs con --progress=plain.
También comprueba el tiempo total y el tamaño de transferencia. Si usas builders distintos, apunta explícitamente con --builder para no crear cachés paralelos innecesarios.
- Usa --progress=plain para logs detallados.
- Valida que cache-ref apunte a la etiqueta correcta.
- Confirma el login en el registry antes del build.
# 1) Aquecer cache
docker buildx build --builder ci-builder \
--cache-to=type=registry,ref=$CACHE_REF,mode=max \
-t $IMAGE --push --progress=plain .
# 2) Segunda execução deve mostrar hits de cache
docker buildx build --builder ci-builder \
--cache-from=type=registry,ref=$CACHE_REF \
-t $IMAGE --push --progress=plain .
7) Problemas comunes y cómo resolverlos
¿El caché no impacta en el install de dependencias? Verifica que los archivos de manifiesto se copien antes del resto del código. Para npm, package-lock.json debe ser estable.
Cambios en ARG o ENV en la parte superior del Dockerfile invalidan las capas siguientes. Fija versiones y ordena instrucciones por estabilidad.
En runners que limpian todo, la caché remota es esencial. docker buildx prune borra caches; ejecútalo solo cuando sea necesario y con parámetros adecuados para preservar espacio.
- Organiza el Dockerfile por estabilidad de capas.
- Fija versiones de base y herramientas.
- Evita ARGs dinámicos en la parte superior.
- Mantén una sola referencia de caché por servicio.
# Limpeza de caches (use com cautela)
# Isso apaga caches locais do builder selecionado
# Confirme o builder atual com: docker buildx ls
docker buildx prune --all --force
Preguntas frecuentes
BuildKit y Buildx son lo mismo?
No. BuildKit es el motor de build. Buildx es la interfaz de línea de comandos que expone funciones avanzadas de BuildKit, como multi‑plataforma, drivers y caché remota. Usas Buildx para activar BuildKit con flags como --cache-from y --cache-to.
Cuándo usar caché remota en lugar del caché local?
En pipelines CI con runners efímeros, el caché local se pierde en cada ejecución. La caché remota vía registry permite reutilizar capas entre jobs y ramas, reduciendo tiempo de build y tráfico. En entornos de desarrollo local, el caché local suele ser suficiente.
La caché remota almacena secretos del build?
No, siempre que no escribas secretos en capas. Usa mounts de secreto de BuildKit para pasar tokens en tiempo de build sin persistirlos en la imagen o en la caché. Evita mostrar secretos en la salida del CI para que no queden en los logs.
Cómo manejar un monorepo con varios servicios?
Crea una referencia de caché por servicio, usando una etiqueta de caché estable por carpeta. Esto evita colisiones entre proyectos y maximiza la reutilización. Estandariza Dockerfiles con multistage y mounts de caché, y comparte una plantilla de pipeline entre servicios.
Por qué mis installs siguen sin caché aun con cache-to/from?
Normalmente hay invalidación por el orden de las instrucciones o manifests inestables. Copia solo package*.json o el archivo de lock antes del resto del código, fija versiones y usa --mount=type=cache en el gestor de paquetes. Revisa los logs con --progress=plain para localizar el primer miss.
Necesito publicar la imagen final para usar caché remota?
No. Puedes exportar solo el caché al registry sin publicar una etiqueta pública de la imagen final. Sin embargo, muchos pipelines ya hacen push de la imagen; en ese caso aprovecha la misma autenticación para enviar y consumir el caché.
Lleva la caché remota a tu CI hoy
Implementa BuildKit y caché remota en tu pipeline y compara los tiempos de build. Complementa después con multistage y buenas prácticas en el Dockerfile.
Explorar más guíasVer también
- Tutorial: implementar Canary releases con Traefik en Docker Compose usando headers y weighted routing para despliegues graduales de aplicaciones PHP
- Tutorial: configurar HAProxy con Let's Encrypt y healthchecks para balanceo de tráfico entre contenedores Docker en VPS (paso a paso)
- Tutorial práctico: implementar mTLS entre Nginx y microservicios usando smallstep CA en Docker Compose
- Tutorial: configurar cgroup v2 y systemd ResourceControl para limitar CPU, memoria e I/O de contenedores Docker en Debian