Acelerar o build de imagem no CI
Na sua máquina, o segundo docker build leva segundos. No CI, leva os mesmos oito minutos
de sempre — porque cada execução começa em um runner limpo, sem nenhuma camada anterior.
Este guia faz o cache sobreviver entre execuções.
1. Entenda por que o cache não persiste
Seção intitulada “1. Entenda por que o cache não persiste”O cache de camadas fica no armazenamento local do daemon Docker. Runner efêmero significa daemon novo a cada execução: nada para reaproveitar. A solução é exportar o cache para um lugar que persiste — o registry, ou o cache do próprio provedor de CI.
2. Exporte o cache para o registry
Seção intitulada “2. Exporte o cache para o registry”docker buildx create --name ci --driver docker-container --use
docker buildx build \ --cache-from type=registry,ref=registry.exemplo.com/loja:buildcache \ --cache-to type=registry,ref=registry.exemplo.com/loja:buildcache,mode=max \ -t registry.exemplo.com/loja:${GIT_SHA} \ --push .mode=max exporta as camadas de todos os estágios, inclusive os intermediários do
multi-stage — é o que realmente acelera. O padrão (mode=min) só guarda o estágio final e
rende bem menos.
No GitHub Actions, há também o cache nativo do runner:
- uses: docker/build-push-action@v6 with: push: true tags: registry.exemplo.com/loja:${{ github.sha }} cache-from: type=gha cache-to: type=gha,mode=maxCompare os dois na sua realidade: type=gha tem cota por repositório e é rápido para
projetos pequenos; o registry não tem essa cota, é compartilhado entre workflows e entre a
máquina de quem desenvolve, e custa armazenamento.
3. Ordene o Dockerfile a favor do cache
Seção intitulada “3. Ordene o Dockerfile a favor do cache”Cache não adianta se toda mudança de código invalida a instalação de dependências. A regra é do que muda menos para o que muda mais.
FROM node:22-slim AS buildWORKDIR /app
# 1. só o manifesto: esta camada muda quando as dependências mudamCOPY package.json package-lock.json ./RUN npm ci
# 2. o código vem depois: mudar código não reinstala nadaCOPY . .RUN npm run buildO erro clássico é COPY . . antes do npm ci / pip install / go mod download: o cache
quebra em todo commit.
Ganho extra com cache de montagem, que preserva o cache do gerenciador de pacotes entre builds sem inchar a imagem:
RUN --mount=type=cache,target=/root/.npm npm ciRUN --mount=type=cache,target=/root/.cache/pip pip install -r requirements.txtRUN --mount=type=cache,target=/go/pkg/mod go mod downloadSome a isso um .dockerignore que exclua .git, node_modules e artefatos: contexto
grande atrasa o build antes mesmo de a primeira camada rodar.
4. Meça o ganho real
Seção intitulada “4. Meça o ganho real”# compare execuções: com cache frio e com cache quentetime docker buildx build --no-cache -t loja:teste . > /dev/nulltime docker buildx build -t loja:teste . > /dev/nullNa saída do buildx, procure por CACHED nas etapas: se as etapas de dependência não
aparecem como cacheadas na segunda execução, a chave ou a ordem estão erradas.
O que observar depois de ligar o cache:
- Etapas de dependência marcadas como
CACHEDem builds seguidos. - Tempo total do job de imagem, antes e depois.
- Tempo gasto exportando o cache — com
mode=maxem imagens grandes, o push do cache pode comer parte do ganho. Se acontecer, testemode=minou limite o cache aos estágios pesados.
Se o cache não acerta
Seção intitulada “Se o cache não acerta”| Sintoma | Causa provável |
|---|---|
Nada CACHED na segunda execução |
Driver padrão em vez de docker-container |
| Cache acerta na base e para no meio | COPY . . cedo demais no Dockerfile |
Acerta em main e nunca em PR |
Escopo do cache por branch — use um cache compartilhado |
| Build multiplataforma sem cache | Cada plataforma tem sua chave; exporte para as duas |
| Cache cresce sem parar no registry | Sem política de retenção na tag buildcache |
| Cache “acerta” com dependência antiga | Lockfile não copiado antes da instalação |
Um cuidado de segurança: o cache é um artefato compartilhado. Não use o cache de builds de
pull requests de terceiros para produzir a imagem de produção, e não deixe segredo entrar
em camada cacheada (use --mount=type=secret, nunca ARG).
Checklist de pronto
Seção intitulada “Checklist de pronto”- Builder
docker-containerno CI. -
cache-fromecache-toconfigurados, commode=max. - Dockerfile ordenado do estável para o volátil.
-
.dockerignorereduzindo o contexto. - Etapas de dependência aparecendo como
CACHED. - Tempo medido antes e depois, incluindo o custo de exportar o cache.
- Retenção definida para a tag de cache no registry.