Fala Galera, beleza ?
Docker já é uma tecnologia consolidada no mercado. E, por isso mesmo, muita gente trata o Dockerfile como algo simples, resolvido, quase automático. Mas é justamente aí que muitos projetos falham.
A forma como você escreve seu Dockerfile pode comprometer:
- o tempo de build
- o tempo de subida dos containers
- a segurança da aplicação
- a rastreabilidade de erros
- o custo com armazenamento
- e até a eficiência dos seus testes e deploys
No newsletter de hoje, vamos passar por algumas práticas fundamentais para garantir um Dockerfile limpo, leve e confiável.
✅ Boas práticas para criar um Dockerfile de qualidade
1. Use imagens base enxutas
Evite imagens genéricas como ubuntu:latest. Prefira versões slim, distroless ou específicas da linguagem. Além disso, tem algumas imagens “alpine” que são ótimas.

2. Seja explícito com versões
Nunca confie em latest. Isso pode comprometer a previsibilidade do build. Já vi de ficarem com Latest até começar a dar problema pois o runtime teve uma atualização!

3. Organize o build para aproveitar cache
No Docker, cada instrução cria uma camada que pode ser reaproveitada em builds futuros.
Se você copiar todo o código antes de instalar as dependências, qualquer pequena alteração (até em um comentário) vai invalidar o cache e forçar a reinstalação de tudo.
A forma correta é copiar primeiro apenas o arquivo de dependências (requirements.txt, package.json, etc.), instalar os pacotes e só depois copiar o restante do código. Assim, o cache das dependências só é refeito quando realmente houver mudanças nelas — acelerando bastante o build.

4. Use .dockerignore
Durante o build, o Docker envia todo o conteúdo do diretório para o contexto de build.
Se você não filtrar nada, isso inclui pastas e arquivos que não têm nenhuma utilidade na imagem, como .git, node_modules, logs ou até arquivos temporários.
Um .dockerignore bem configurado:
- acelera o build, porque menos dados são enviados;
- reduz o tamanho da imagem, já que nada desnecessário vai parar lá dentro;
- evita vazamentos de informações sensíveis, como chaves, configs locais ou históricos de código.
Exemplo simples de .dockerignore:

5. Agrupe comandos em um único RUN
Cada instrução RUN no Docker cria uma nova camada na imagem.
Se você espalhar várias instruções (RUN apt-get update, depois outro RUN apt-get install, e assim por diante), vai acabar com muitas camadas extras, aumentando o tamanho final da imagem e dificultando sua manutenção.
A prática recomendada é agrupar os comandos relacionados em uma única instrução e sempre limpar caches temporários ao final, garantindo imagens mais leves e otimizadas:

Assim você:
- mantém menos camadas (famosas layers) no build;
- reduz o tamanho da imagem;
- evita acúmulo de arquivos temporários que nunca seriam usados no container em execução.
6. Defina ENTRYPOINT e CMD corretamente
No Docker, ENTRYPOINT e CMD têm papéis diferentes, mas complementares:
ENTRYPOINTdefine o comando principal, que sempre será executado.CMDdefine parâmetros ou valores padrão, que podem ser sobrescritos na hora de rodar o container.
Usando os dois juntos, você cria imagens mais flexíveis e previsíveis.
Exemplo:

Nesse caso:
- O container sempre vai iniciar o interpretador Python (
ENTRYPOINT). - Por padrão, ele executa o
app.py(CMD). - Se precisar, você pode sobrescrever apenas o argumento ao rodar o container:

Assim, o ENTRYPOINT garante consistência e o CMD dá flexibilidade.
7. Configure HEALTHCHECK
O HEALTHCHECK garante que o Docker saiba se o container realmente está funcionando.
Sem ele, o container pode estar “rodando”, mas indisponível.
Exemplo:

Isso permite que orquestradores (como Docker Swarm ou Kubernetes) reiniciem ou removam containers com falhas automaticamente.
8. Nunca exponha variáveis sensíveis
Nunca coloque secrets, tokens ou credenciais direto no Dockerfile ou em instruções como ENV.
Essas informações ficam registradas nas camadas da imagem e podem ser recuperadas facilmente. (Lembra do 12-factor app ?!)
O ideal é:
- Usar variáveis de ambiente definidas na hora do deploy;
- Ou adotar ferramentas próprias para gestão de segredos, como Vault, AWS Secrets Manager, SSM, GCP Secret Manager.
Assim, você mantém a imagem segura e evita expor dados críticos.
9. Use multi-stage builds ← USEEE MESMO!
O multi-stage build permite separar o processo de build do de execução, deixando a imagem final mais leve e segura.
Exemplo:

👉 Note que no exemplo acima usamos uma imagem maior (golang:1.20) para compilar a aplicação e depois copiamos apenas o binário final para uma imagem menor (golang:1.20-alpine).
Benefícios:
- A imagem final contém apenas o necessário para rodar.
- Bibliotecas e ferramentas de compilação não vão para produção.
- Reduz drasticamente o tamanho da imagem e a superfície de ataque.
Conclusão
O Dockerfile pode parecer só um detalhe técnico, mas ele define muito da performance, segurança e previsibilidade do seu sistema.
Um arquivo bem escrito evita custos desnecessários, acelera seus ciclos de entrega e aumenta a confiança nos ambientes de produção.
No fim das contas, não é só sobre “rodar um container”. É sobre criar uma base sólida para que sua aplicação seja realmente moderna, resiliente e de alta qualidade.
👉 Caso queira se aprofundar em temas como Site Reliability Engineering, 12-Factor App e muitas outras práticas de arquitetura moderna, vale conhecer a Comunidade de Arquitetura Descomplicada (CaD).