Seu build reconstrói tudo, toda vez

Cole o Dockerfile e veja qual linha quebra o cache a cada mudança de código — e o que está inflando a imagem.

Roda no seu navegador · nada é enviado para servidor

Cole o Dockerfile

A regra do cache, em uma frase

O Docker guarda o resultado de cada instrução. Quando você constrói de novo, ele reaproveita as camadas até a primeira que mudou — e a partir dali refaz tudo, mesmo o que continua idêntico.

Para COPY e ADD a conta inclui o conteúdo dos arquivos. É por isso que uma linha só no lugar errado custa minutos em cada build.

O erro que faz o npm install rodar sempre

Este é o campeão absoluto, e cabe em duas linhas:

COPY . .              ← copia TODO o código
RUN npm ci            ← e só depois instala

Você mudou uma vírgula num arquivo qualquer. O COPY . . percebe, invalida a camada — e o npm ci, que está abaixo, refaz do zero. Toda. Santa. Vez.

A inversão resolve, e é a otimização de melhor retorno em Docker:

COPY package*.json ./     ← só o manifesto, que muda pouco
RUN npm ci                ← esta camada vira cache de longa duração
COPY . .                  ← o código depois

Agora mudar código invalida só a última linha. O npm ci só roda de novo quando uma dependência realmente muda — que é o comportamento que você esperava desde o começo.

Multi-stage: o que corta mais tamanho

A imagem final não precisa do compilador, do TypeScript, nem das dependências de desenvolvimento. Com multi-stage você constrói num estágio e leva só o resultado para o outro:

FROM node:22-slim AS build
WORKDIR /app
COPY package*.json ./
RUN npm ci                       # com devDependencies, para compilar
COPY . .
RUN npm run build

FROM node:22-slim
WORKDIR /app
ENV NODE_ENV=production
COPY package*.json ./
RUN npm ci --omit=dev            # só o que roda em produção
COPY --from=build /app/dist ./dist
USER node
CMD ["node", "dist/index.js"]

O que fica de fora da imagem final: o código-fonte, as ferramentas de build, o cache do npm e as devDependencies. Em projeto Node típico é a diferença entre centenas de megabytes e algumas dezenas.

As três linhas que incham sem você ver

1. apt-get sem limpeza no mesmo RUN

Camada é imutável: apagar arquivo num RUN seguinte não diminui a imagem, só adiciona uma camada dizendo que o arquivo sumiu. O peso continua lá. A limpeza precisa acontecer no mesmo RUN que criou a sujeira:

RUN apt-get update \
 && apt-get install -y --no-install-recommends curl \
 && rm -rf /var/lib/apt/lists/*

No Alpine o equivalente é o --no-cache, que evita gravar o índice: apk add --no-cache curl.

2. apt-get update separado do install

Se o update está numa camada e o install em outra, o Docker pode reaproveitar um índice velho e instalar pacote que não existe mais — o famoso 404 Not Found que aparece do nada meses depois. Os dois no mesmo RUN, sempre.

3. FROM sem versão fixa

node:latest parece cômodo e é o oposto: a imagem muda embaixo de você, e o build que passou ontem quebra hoje sem ninguém ter mexido em nada. Fixe a versão — e prefira -slim ou -alpine, que já cortam bastante.

O .dockerignore que quase ninguém escreve

Sem ele, o COPY . . leva junto o node_modules da sua máquina, a pasta .git inteira e todo arquivo local de ambiente. Isso engorda a imagem, atrasa o build e — pior — pode vazar segredo para dentro dela.

node_modules
.git
.env
.env.*
dist
coverage
*.log
Segredo em ARG ou ENV fica na imagem. Quem tiver a imagem lê com docker history, mesmo que a linha seguinte apague a variável. Para segredo em build, use --mount=type=secret; para execução, passe na hora de subir o container.

Rodar como root é o padrão — e não devia ser

Sem USER, o processo dentro do container roda como root. Se alguém escapar da aplicação, começa com o poder máximo. As imagens oficiais do Node já trazem um usuário pronto:

USER node
CMD ["node", "dist/index.js"]

Se o container sobe mas o proxy não alcança, o problema costuma ser endereço: está no diagnóstico de Nginx 502 e no artigo de proxy reverso.

Newsletter

Receba os artigos novos por e-mail

Sem spam. Só o aviso quando sai um tutorial ou artigo novo.