Docker Compose para Desenvolvimento Local: Setup Profissional
“Funciona na minha máquina” é uma frase que Docker acabou com quase todas as desculpas para usar. Mas um docker-compose.yml mal configurado cria novos problemas: volumes lentos, hot reload quebrado, banco que some a cada restart.
O arquivo base
# docker-compose.yml
version: '3.9'
services:
# ── Aplicação ──────────────────────────────────────────
api:
build:
context: .
dockerfile: Dockerfile.dev
ports:
- "3000:3000"
volumes:
- .:/app # código-fonte montado
- /app/node_modules # node_modules do container (não sobrescrito)
environment:
- NODE_ENV=development
- DATABASE_URL=postgresql://dev:dev@postgres:5432/devdb
- REDIS_URL=redis://redis:6379
depends_on:
postgres:
condition: service_healthy
redis:
condition: service_started
restart: unless-stopped
# ── PostgreSQL ─────────────────────────────────────────
postgres:
image: postgres:16-alpine
environment:
POSTGRES_USER: dev
POSTGRES_PASSWORD: dev
POSTGRES_DB: devdb
ports:
- "5432:5432"
volumes:
- postgres_data:/var/lib/postgresql/data
- ./scripts/init.sql:/docker-entrypoint-initdb.d/init.sql
healthcheck:
test: ["CMD-SHELL", "pg_isready -U dev -d devdb"]
interval: 5s
timeout: 5s
retries: 5
restart: unless-stopped
# ── Redis ──────────────────────────────────────────────
redis:
image: redis:7-alpine
command: redis-server --appendonly yes
ports:
- "6379:6379"
volumes:
- redis_data:/data
restart: unless-stopped
# ── Redis UI (desenvolvimento) ─────────────────────────
redis-commander:
image: rediscommander/redis-commander:latest
environment:
REDIS_HOSTS: "local:redis:6379"
ports:
- "8081:8081"
depends_on: [redis]
profiles: ["tools"] # só sobe com: docker compose --profile tools up
volumes:
postgres_data:
redis_data:Dockerfile.dev com hot reload
# Dockerfile.dev
FROM node:20-alpine
WORKDIR /app
# Instala dependências em camada separada (cache)
COPY package*.json ./
RUN npm ci
# Não copia o código — vem do volume em dev
# COPY . . ← só no Dockerfile de produção
EXPOSE 3000
# nodemon para hot reload
CMD ["npx", "nodemon", "--watch", "src", "--ext", "ts,js", \
"--exec", "node --loader ts-node/esm src/server.ts"]O problema do node_modules no volume
Esse é o erro mais comum:
volumes:
- .:/app # monta tudo, incluindo seu node_modules local (Windows!)Em macOS/Windows, seu node_modules local tem binários compilados para o seu OS. Quando montado no container Linux, quebra.
A solução é um volume anônimo que “mascara” o node_modules local:
volumes:
- .:/app # monta o código
- /app/node_modules # volume anônimo — isola o node_modules do containerO Docker cria um volume separado para /app/node_modules que sobrepõe o diretório montado. O container usa seus próprios módulos compilados para Linux.
Variáveis de ambiente com .env
# .env.docker (não commitar .env com secrets!)
POSTGRES_PASSWORD=dev
JWT_SECRET=development-secret-only# docker-compose.yml
services:
api:
env_file:
- .env.docker
environment:
- NODE_ENV=development # sobrescreve .env.docker se necessário# .gitignore
.env
.env.local
.env.*.local
# NÃO ignorar:
.env.docker.example # arquivo de exemplo versionadoComandos do dia a dia
# Sobe tudo em background
docker compose up -d
# Sobe com ferramentas extras (redis-commander, etc.)
docker compose --profile tools up -d
# Logs de um serviço em tempo real
docker compose logs -f api
# Executa comando dentro do container
docker compose exec api npm run db:migrate
docker compose exec api sh # shell interativo
# Rebuild após mudar Dockerfile ou package.json
docker compose up -d --build api
# Para tudo mas mantém os volumes
docker compose stop
# Para e remove containers (volumes persistem)
docker compose down
# Para, remove containers E volumes (reset total)
docker compose down -vOverride para diferentes contextos
# docker-compose.override.yml (automático em desenvolvimento)
services:
api:
environment:
- DEBUG=* # verbose logging só no dev
# docker-compose.ci.yml (para CI/CD)
# docker compose -f docker-compose.yml -f docker-compose.ci.yml up
services:
api:
environment:
- NODE_ENV=testHealthchecks e ordem de inicialização
depends_on:
postgres:
condition: service_healthy # espera o healthcheck passarSem condition: service_healthy, o container da API sobe antes do banco estar pronto para aceitar conexões — e sua aplicação falha na inicialização. O healthcheck do postgres com pg_isready resolve isso.
Performance em macOS com volumes
Volumes no macOS são notoriamente lentos. Para projetos grandes:
# Para arquivos que não precisam de sync imediato
volumes:
- .:/app:cached # leituras usam cache do host (mais rápido)
- /app/node_modules # isoladoNo Docker Desktop, ative “VirtioFS” nas configurações para melhor performance em macOS com Apple Silicon.
Commit o docker-compose.yml no repositório. Todo novo dev rodando docker compose up tem o ambiente completo funcionando em minutos — sem “funciona na minha máquina”.