O “funciona na minha máquina” é o inimigo da produtividade. Docker Compose resolve isso definitivamente: um arquivo docker-compose.yml descreve todos os serviços que sua aplicação precisa — app, banco de dados, cache, fila de mensagens — e um único comando sobe tudo configurado e conectado. Novo desenvolvedor no time? docker compose up e está trabalhando em 5 minutos.
Anatomia do docker-compose.yml
O arquivo define services (containers), networks (comunicação entre eles), e volumes (persistência de dados). Um setup típico para aplicação web inclui: o app (build a partir do Dockerfile do projeto), PostgreSQL (imagem oficial, volume para dados, variáveis de ambiente para credenciais), Redis (imagem oficial, sem persistência para cache de desenvolvimento), e possivelmente Nginx como reverse proxy.
Cada serviço define: image ou build context, ports para expor ao host, environment para variáveis, volumes para montar código ou dados, depends_on para ordem de startup, e healthcheck para garantir que o serviço está realmente pronto (não apenas rodando). O depends_on com condition: service_healthy garante que sua aplicação só inicia depois que o PostgreSQL está aceitando conexões — eliminando race conditions de startup.
Hot reload com volumes
Para desenvolvimento, monte seu código fonte como volume no container: ./src:/app/src. Alterações no código local refletem instantaneamente no container sem rebuild. Para Node.js, combine com nodemon ou tsx –watch. Para Python, use watchdog ou uvicorn –reload. Para PHP, fpm serve mudanças automaticamente. O workflow ideal: edite no VS Code, salve, veja a mudança no browser — tudo rodando dentro do container com as mesmas versões que produção.
Cuidado com node_modules em volumes: montar o diretório completo da aplicação sobrescreve o node_modules instalado no container. A solução é usar anonymous volume para node_modules: /app/node_modules como volume Docker separado, mantendo as dependências instaladas no build mas permitindo hot reload do código fonte. Alternativamente, instale dependências no host e monte tudo — funciona quando as versões do Node são iguais.
Profiles e ambientes
Docker Compose profiles permitem agrupar serviços opcionais: um profile “debug” inclui pgAdmin e Redis Commander para visualizar dados, um profile “monitoring” inclui Prometheus e Grafana, um profile “test” inclui o runner de testes e banco de dados limpo. Ative seletivamente: docker compose –profile debug up sobe o app + ferramentas de debug sem carregar monitoring desnecessariamente.
Para múltiplos ambientes, use docker-compose.override.yml (carregado automaticamente em desenvolvimento) com configurações de debug, ports expostas e volumes de código. O docker-compose.yml base define a estrutura de produção, e o override adiciona conveniências de desenvolvimento. Para CI, docker-compose.test.yml define um ambiente limpo com banco vazio e service dependencies mínimas para execução rápida de testes.
Multi-stage builds e otimização
Dockerfiles multi-stage separam build de runtime: o primeiro stage instala dependências, compila código, e gera artefatos. O segundo stage usa uma imagem base mínima (alpine, distroless) e copia apenas os artefatos necessários. O resultado: imagens de 50MB em vez de 1GB, com superfície de ataque reduzida e startup mais rápido. Para Node.js, o primeiro stage faz npm ci e npm run build, o segundo copia apenas node_modules production e a pasta dist.
Docker layer caching é essencial para build speed: ordene instruções no Dockerfile do menos para o mais frequentemente alterado. COPY package.json antes de COPY src/ garante que npm install é cached quando apenas o código fonte muda (não as dependências). Em CI/CD, use cache mounts e BuildKit para persistir cache entre builds. Um build que levaria 5 minutos passa a levar 30 segundos quando apenas o código da aplicação mudou.
Tem um projeto em mente?
Somos especialistas em transformar ideias em produtos digitais. Apps, sites, automações e IA — vamos construir juntos.