Reduzir o tamanho de uma imagem Docker
Imagem grande custa em três lugares: tempo de push e pull em cada deploy, armazenamento no registry, e superfície de ataque — cada pacote instalado é um CVE em potencial. Este guia percorre as reduções na ordem de retorno, medindo antes e depois.
1. Meça o tamanho e onde ele está
Seção intitulada “1. Meça o tamanho e onde ele está”docker images loja --format '{{.Size}}'docker history loja:atual --no-trunc --format '{{.Size}}\t{{.CreatedBy}}' | head -20docker history mostra o peso por camada e quase sempre aponta o culpado na primeira
linha: a imagem base, o gerenciador de pacotes, ou um COPY que trouxe o repositório
inteiro.
Para uma análise mais detalhada, o dive mostra o desperdício por camada:
dive loja:atual2. Troque a imagem base
Seção intitulada “2. Troque a imagem base”O maior ganho isolado, e o mais fácil.
| Base | Ordem de grandeza | Quando usar |
|---|---|---|
ubuntu, debian |
~75–120 MB | precisa de muitas ferramentas do sistema |
-slim |
~30–80 MB | bom padrão para linguagens interpretadas |
alpine |
~5–15 MB | precisa de shell; atenção ao musl |
distroless |
~2–20 MB | binário compilado; sem shell nem gerenciador |
scratch |
0 | binário estático (Go, Rust) |
Duas ressalvas honestas: Alpine usa musl em vez de glibc, o que pode mudar o
comportamento de DNS e quebrar bibliotecas nativas — e, em Python, forçar compilação de
pacotes que teriam wheel pronto, aumentando o tempo de build. Distroless não tem shell, o
que é ótimo para segurança e exige kubectl debug com container efêmero para diagnosticar.
3. Separe build de runtime com multi-stage
Seção intitulada “3. Separe build de runtime com multi-stage”A imagem final não precisa do compilador, das ferramentas de teste nem das dependências de desenvolvimento.
# ---- build ----FROM golang:1.23 AS buildWORKDIR /srcCOPY go.mod go.sum ./RUN go mod download # camada cacheável: só muda se as deps mudaremCOPY . .RUN CGO_ENABLED=0 go build -ldflags="-s -w" -o /bin/loja ./cmd/loja
# ---- runtime ----FROM gcr.io/distroless/static-debian12:nonrootCOPY --from=build /bin/loja /bin/lojaUSER nonroot:nonrootENTRYPOINT ["/bin/loja"]Para linguagens interpretadas, o mesmo princípio: instale dependências em um estágio e copie só o que o runtime precisa.
FROM python:3.13-slim AS buildWORKDIR /appCOPY requirements.txt .RUN pip install --no-cache-dir --prefix=/instalado -r requirements.txt
FROM python:3.13-slimCOPY --from=build /instalado /usr/localCOPY app/ /app/WORKDIR /appUSER 10001CMD ["python", "-m", "app"]4. Limpe na mesma camada
Seção intitulada “4. Limpe na mesma camada”Cada instrução cria uma camada. Apagar em uma camada seguinte não reduz o tamanho final — o dado continua na camada anterior.
# errado: o cache do apt continua na imagemRUN apt-get update && apt-get install -y curlRUN rm -rf /var/lib/apt/lists/*
# certo: instala e limpa na mesma camadaRUN apt-get update \ && apt-get install -y --no-install-recommends curl \ && rm -rf /var/lib/apt/lists/*Equivalentes por ecossistema: --no-cache no apk, --no-cache-dir no pip,
npm ci --omit=dev seguido de npm cache clean --force.
5. Use .dockerignore
Seção intitulada “5. Use .dockerignore”Sem ele, o contexto inteiro vai para o daemon — inclusive .git, node_modules local e
artefatos de teste. Isso incha a imagem e deixa o build lento.
.gitnode_modulesdist*.log.env.terraformtests/fixtures/grandesO .env na lista não é detalhe estético: segredo copiado para uma camada permanece na
imagem mesmo que você o apague depois.
6. Confira o resultado
Seção intitulada “6. Confira o resultado”docker build -t loja:novo .docker images loja --format '{{.Tag}}\t{{.Size}}'docker history loja:novo --format '{{.Size}}\t{{.CreatedBy}}' | head -10
# e confirme que continua funcionando — o objetivo não é a menor imagem, é a menor que rodadocker run --rm loja:novo --versiontrivy image --severity HIGH,CRITICAL loja:novo # a redução de CVEs costuma ser grandeMeça também o tempo de build: multi-stage bem ordenado costuma acelerar, mas trocar a base pode desacelerar (compilação de dependências). Se o build ficou muito mais lento, veja cache de build no CI.
Se der errado
Seção intitulada “Se der errado”| Sintoma | Causa provável |
|---|---|
exec format error |
Binário compilado para outra arquitetura |
not found ao rodar binário existente |
Binário dinâmico em scratch/distroless — compile estático |
| Erro de DNS ou de SSL só no Alpine | musl, ou faltam ca-certificates |
permission denied |
Usuário não-root sem permissão no caminho; ajuste no build |
| Imagem menor, mas build muito mais lento | Ordem do Dockerfile invalidando o cache |
Checklist de pronto
Seção intitulada “Checklist de pronto”- Tamanho medido antes e depois.
- Base mínima adequada à stack.
- Multi-stage separando build de runtime.
- Limpeza feita na mesma camada da instalação.
-
.dockerignorecobrindo.git, dependências locais e segredos. - Container executando como usuário não-root.
- Aplicação testada na imagem nova.