Docker: reduzindo imagem de 1GB para 50MB na prática

Tabela de Conteúdo

Imagem Docker de 1GB? Deploy demorando 10 minutos? Vamos resolver isso.

O primeiro passo é entender exatamente o que está deixando sua imagem gigante. Antes de otimizar, precisamos diagnosticar o problema:

Diagnóstico rápido

# Ver tamanho das imagens
$ docker images --format "table {{.Repository}}\t{{.Size}}\t{{.Tag}}"

# Ver layers da imagem
$ docker history <imagem>:tag

Problemas comuns que deixam imagem gigante

1) Usar imagem base Ubuntu/Debian

# NÃO FAÇA ISSO
FROM ubuntu:22.04
RUN apt-get update && apt-get install -y nodejs npm

Resultado: ~800MB só do base

2) Copiar tudo antes de instalar dependências

# NÃO FAÇA ISSO
COPY . .
RUN npm install

Resultado: Cache invalidado toda vez

3) Não usar .dockerignore

Resultado: node_modules, .git, logs vão para imagem

Soluções práticas

Vamos resolver esses problemas com técnicas que funcionam na prática:

Solução 1: Multistage build

Antes (1.2GB)

FROM node:18
WORKDIR /app
COPY package*.json ./
RUN npm install
COPY . .
RUN npm run build
EXPOSE 3000
CMD ["npm", "start"]

Depois (45MB)

# Stage 1: Build
FROM node:18-alpine AS builder
WORKDIR /app
COPY package*.json ./
RUN npm ci --only=production
COPY . .
RUN npm run build

# Stage 2: Runtime
FROM node:18-alpine AS runtime
WORKDIR /app
COPY --from=builder /app/node_modules ./node_modules
COPY --from=builder /app/dist ./dist
COPY --from=builder /app/package.json ./package.json

# Remove usuário não essencial
RUN addgroup -g 1001 -S nodejs
RUN adduser -S nextjs -u 1001
USER nextjs

EXPOSE 3000
CMD ["npm", "start"]

Solução 2: .dockerignore

.dockerignore

# .dockerignore
node_modules
npm-debug.log
.git
.gitignore
README.md
.env
.nyc_output
coverage
.nyc_output
.coverage
.cache
dist

Solução 3: Alpine vs Distroless

Alpine (45MB)

FROM node:18-alpine
RUN apk add --no-cache dumb-init

Distroless (25MB)

FROM node:18 AS builder
# ... build stage

FROM gcr.io/distroless/nodejs18
COPY --from=builder /app/dist ./dist

Comandos úteis do dia a dia

# Build com cache otimizado
$ docker build --build-arg BUILDKIT_INLINE_CACHE=1 --cache-from <imagem>:latest -t <imagem>:latest .

# Ver tamanho de cada layer
$ docker history --human --format "table {{.CreatedBy}}\t{{.Size}}" <imagem>:tag

# Limpar cache do Docker
$ docker builder prune -f

# Analisar imagem com dive
$ dive <imagem>:tag

# Ver vulnerabilidades
$ docker scan <imagem>:tag

Resultado esperado

Com as técnicas deste post, você pode reduzir uma imagem Node.js:

  • Imagem base ubuntu: ~800MB → Alpine: ~50MB
  • Sem .dockerignore: +200-500MB → Com .dockerignore: economia de 200-500MB
  • Single stage: build tools incluídos → Multistage: só runtime

Resultado típico: 1GB+ → 30-60MB (redução de 90-95%)

Dica final

Comece pelo .dockerignore. Muitas vezes só isso já reduz 200-300MB.

Depois use multistage. Só isso já resolve 90% dos casos.

Simples assim! :)