Voltar para o inícioDocker

Como implementar multi-stage builds em imagens de produção: guia prático para produção

Aprenda a implementar multi-stage builds Docker para separar compilação e runtime, reduzir imagens de produção e fortalecer a segurança dos containers.

M

Max Alex

Como implementar multi-stage builds em imagens de produção: guia prático para produção

Imagens Docker grandes acumulam compiladores, gerenciadores de pacotes e arquivos temporários que não deveriam chegar ao ambiente produtivo. Os multi-stage builds Docker resolvem esse problema ao separar as etapas de compilação, teste e execução no mesmo Dockerfile.

Neste guia, você vai criar imagens de produção menores, com menos componentes expostos e mais simples de distribuir em pipelines e registries.

O que são multi-stage builds no Docker

Um multi-stage build é um Dockerfile com mais de uma instrução FROM. Cada FROM inicia um estágio independente, com sua própria imagem base, ferramentas e arquivos.

Normalmente, o primeiro estágio reúne tudo o que a aplicação precisa para compilar ou empacotar: Node.js, Maven, GCC, dependências de desenvolvimento e testes. O último estágio contém somente o runtime e os artefatos necessários para executar a aplicação.

A instrução COPY --from=nome-do-estagio permite copiar arquivos selecionados entre estágios. Assim, dependências de compilação não precisam fazer parte da imagem distribuída para produção.

Por exemplo, uma aplicação front-end pode ser compilada com uma imagem completa do Node.js e ter apenas a pasta dist copiada para uma imagem enxuta do Nginx.

Por que usar multi-stage builds em imagens de produção

O principal benefício é reduzir o tamanho da imagem final. Menos dados para baixar, armazenar e transferir tornam builds e deployments mais rápidos, especialmente quando a equipe trabalha com múltiplos ambientes ou autoscaling.

A abordagem também reduz a superfície de ataque. Ferramentas como compiladores, npm, Maven e arquivos de cache ficam no estágio de build, em vez de permanecerem disponíveis no container em produção. Esse cuidado complementa boas práticas de segurança em containers.

Além disso, a separação torna as responsabilidades explícitas: um estágio gera o artefato; outro o executa. Isso facilita revisões de Dockerfile e reduz a chance de promover arquivos desnecessários ao registry.

Como planejar os estágios do Dockerfile

Antes de escrever o Dockerfile, liste o que é necessário para compilar, testar e executar a aplicação. A pergunta central é simples: quais arquivos e bibliotecas realmente precisam existir quando o container iniciar?

Em uma API, um desenho comum inclui os estágios builder, test e runtime. O builder instala dependências e gera o artefato; o estágio de teste valida o resultado; o runtime recebe somente o artefato aprovado e suas dependências essenciais.

Nomeie os estágios com AS, escolha imagens base adequadas para cada responsabilidade e evite copiar o projeto inteiro para a imagem final. Em especial, diferencie dependências de desenvolvimento das necessárias em execução.

Também vale definir desde o início quais diretórios devem ser ignorados pelo contexto de build, como .git, logs locais, caches, arquivos de ambiente e dependências geradas na máquina do desenvolvedor.

Implementando um multi-stage build passo a passo

O exemplo abaixo representa uma aplicação Node.js que gera arquivos estáticos em dist. O primeiro estágio instala as dependências e executa o build. O segundo usa uma imagem de runtime e recebe apenas o resultado.

FROM node:22-alpine AS builder
WORKDIR /app

COPY package*.json ./
RUN npm ci

COPY . .
RUN npm run build

FROM nginx:alpine AS runtime
COPY --from=builder /app/dist /usr/share/nginx/html

EXPOSE 80
CMD ["nginx", "-g", "daemon off;"]

O estágio builder nunca é enviado como imagem final quando esse Dockerfile é construído normalmente. A imagem resultante é baseada no Nginx e contém apenas os arquivos compilados em dist.

Para aplicações que precisam de servidor próprio, o estágio final deve declarar um WORKDIR, executar com um USER sem privilégios quando a imagem permitir e definir um CMD claro. O objetivo é que a imagem final tenha somente o necessário para iniciar o serviço.

Construa e execute localmente para validar o fluxo:

docker build -t minha-aplicacao:local .
docker run --rm -p 8080:80 minha-aplicacao:local

Exemplos de multi-stage builds para linguagens populares

Node.js

Para aplicações front-end, copie apenas os arquivos compilados para Nginx ou outro servidor estático. Em APIs, instale dependências de produção no estágio apropriado e não leve ferramentas de build, testes ou documentação para o runtime.

Python

Em Python, o builder pode gerar wheels ou preparar um ambiente virtual. A imagem final deve receber apenas as dependências instaladas e o código necessário para iniciar o serviço. Essa separação é particularmente útil ao padronizar containers Python em produção.

Java

Use Maven ou Gradle no estágio de build para gerar o arquivo JAR. Depois, copie somente o JAR para uma imagem com o JRE compatível. Dessa forma, o runtime não precisa carregar o gerenciador de build nem o código-fonte completo.

Go

Go é um caso especialmente favorável: o builder compila um binário e a imagem final pode conter somente esse binário e os arquivos indispensáveis. Verifique dependências dinâmicas, certificados e configuração de rede antes de optar por uma base muito mínima.

Erros comuns ao criar imagens multi-stage

Um erro recorrente é copiar todo o diretório do builder para o runtime. Isso leva node_modules, caches, código-fonte e arquivos temporários para a imagem final, anulando boa parte do benefício.

Prefira cópias específicas, como COPY --from=builder /app/dist ... ou COPY --from=builder /app/target/app.jar .... Revise também as permissões do arquivo copiado, principalmente se o container final usar um usuário sem privilégios.

Outro problema é não usar .dockerignore. Sem ele, arquivos locais desnecessários entram no contexto enviado ao daemon Docker, aumentando o tempo de build e podendo expor informações indevidas.

Não grave segredos, chaves privadas ou arquivos .env dentro da imagem. Variáveis sensíveis devem ser fornecidas pelo mecanismo de configuração do ambiente e nunca incorporadas em uma camada do Dockerfile.

Por fim, teste o runtime de verdade. Uma imagem mínima pode falhar se bibliotecas do sistema, certificados, fuso horário ou arquivos estáticos necessários não tiverem sido incluídos.

Como validar e medir a imagem final

Meça o resultado antes de publicar. Compare a versão anterior e a nova com docker image ls; isso confirma se a redução de tamanho esperada ocorreu.

docker image ls
docker history minha-aplicacao:local
docker run --rm minha-aplicacao:local

Use docker history para identificar camadas excessivas e inspecione a imagem final para confirmar que ferramentas de build não permaneceram nela. Em seguida, execute um teste de smoke: suba o container, verifique o endpoint de saúde ou a rota principal e confirme os logs de inicialização.

No CI/CD, automatize build, testes, análise de vulnerabilidades e publicação. Use tags imutáveis, como uma versão ou o identificador do commit, para que seja possível rastrear exatamente qual imagem foi implantada.

Uma validação útil é tentar iniciar a imagem em um ambiente limpo. Se ela depender de arquivos externos não documentados ou de comportamento da máquina do desenvolvedor, o problema aparecerá antes do deploy.

Checklist para colocar multi-stage builds em produção

  • Separe build, teste e runtime quando isso fizer sentido para a aplicação.
  • Copie para o estágio final somente artefatos e dependências essenciais.
  • Use uma imagem base adequada e mantenha versões críticas controladas.
  • Inclua um arquivo .dockerignore bem definido.
  • Não inclua segredos, caches, código desnecessário ou ferramentas de compilação na imagem final.
  • Execute o processo com usuário sem privilégios sempre que possível.
  • Teste a inicialização e o funcionamento do container antes da publicação.
  • Automatize análise de vulnerabilidades e testes no pipeline.
  • Publique tags imutáveis e documente o padrão adotado pela equipe.

Multi-stage builds não são apenas uma técnica para diminuir megabytes. Eles ajudam a tornar o processo de entrega mais previsível, reduzem componentes expostos e deixam claro o que a aplicação realmente precisa para rodar.

Quer tornar seus containers mais leves, seguros e prontos para escalar? Conte com a Max Alex para revisar sua arquitetura Docker, automatizar pipelines e estruturar uma operação de produção mais confiável.

M

Max Alex

Criador de conteúdo apaixonado por tecnologia e inovação. Acompanhe nossos artigos para ficar por dentro das melhores estratégias digitais.

Continue lendo