O Docker Compose para Node.js permite definir API, PostgreSQL, Redis, workers, proxies e ferramentas de observabilidade em um único arquivo YAML. Com um comando, a equipe cria redes, volumes e containers com configuração reproduzível para desenvolvimento, testes e ambientes controlados.
Compose simplifica a execução local, mas não corrige automaticamente uma imagem ruim ou uma aplicação que ignora sinais. É necessário definir healthchecks, volumes adequados, variáveis, secrets, limites e dependências. Também é importante separar conveniência de desenvolvimento das práticas usadas em produção.
Neste guia, você aprenderá a criar um compose.yaml para Node.js, configurar PostgreSQL e Redis, usar healthchecks, networks, volumes, profiles, watch, múltiplos arquivos, secrets, shutdown e CI.
O que é Docker Compose?
A documentação oficial do Docker Compose define a ferramenta como uma forma de declarar e executar aplicações com múltiplos containers. Serviços, redes, volumes e configurações ficam em um arquivo YAML.
A referência completa é a Compose Specification.
Estrutura do projeto
project/
├── compose.yaml
├── compose.override.yaml
├── Dockerfile
├── package.json
├── package-lock.json
├── src/
└── scripts/
└── wait-for-migrations.jsO arquivo principal contém a base compartilhada. Overrides adicionam configurações de desenvolvimento ou produção.
Primeiro compose.yaml
services:
api:
build:
context: .
target: development
command: npm run dev
ports:
- "3000:3000"
environment:
NODE_ENV: development
PORT: 3000
DATABASE_URL: postgresql://app:app@postgres:5432/app
REDIS_URL: redis://redis:6379
depends_on:
postgres:
condition: service_healthy
redis:
condition: service_healthy
postgres:
image: postgres:17
environment:
POSTGRES_USER: app
POSTGRES_PASSWORD: app
POSTGRES_DB: app
volumes:
- postgres_data:/var/lib/postgresql/data
healthcheck:
test: ["CMD-SHELL", "pg_isready -U app -d app"]
interval: 5s
timeout: 3s
retries: 10
redis:
image: redis:8
command: redis-server --appendonly yes
volumes:
- redis_data:/data
healthcheck:
test: ["CMD", "redis-cli", "ping"]
interval: 5s
timeout: 3s
retries: 10
volumes:
postgres_data:
redis_data:Iniciando os serviços
docker compose upEm background:
docker compose up -dConstruindo novamente:
docker compose up --buildNome dos serviços como DNS
Dentro da rede do projeto, a API acessa:
postgres:5432
redis:6379localhost dentro do container aponta para o próprio container, não para outro serviço.
Portas
ports:
- "3000:3000"O primeiro número é a porta no host; o segundo, no container. PostgreSQL não precisa ser publicado quando somente a aplicação o utiliza. Remover portas desnecessárias reduz exposição.
Expose
expose:
- "3000"expose documenta a porta entre containers, mas não publica no host. A comunicação na rede Compose já funciona sem ele quando o serviço escuta corretamente.
Dockerfile multi-stage
FROM node:22-bookworm-slim AS dependencies
WORKDIR /app
COPY package.json package-lock.json ./
RUN npm ci
FROM dependencies AS development
COPY . .
CMD ["npm", "run", "dev"]
FROM dependencies AS build
COPY . .
RUN npm run build
RUN npm prune --omit=dev
FROM node:22-bookworm-slim AS production
ENV NODE_ENV=production
WORKDIR /app
USER node
COPY --from=build --chown=node:node /app/node_modules ./node_modules
COPY --from=build --chown=node:node /app/dist ./dist
COPY --from=build --chown=node:node /app/package.json ./package.json
CMD ["node", "--enable-source-maps", "dist/server.js"]Consulte Docker Multi-stage para Node.js.
.dockerignore
node_modules
dist
coverage
.git
.env
*.log
compose*.yaml
Não envie secrets, dependências locais e histórico Git ao build context.
Bind mount em desenvolvimento
services:
api:
volumes:
- .:/app
- node_modules:/app/node_modules
volumes:
node_modules:O volume separado evita que o node_modules do host substitua dependências Linux do container.
Compose Watch
Versões atuais oferecem watch:
services:
api:
develop:
watch:
- action: sync
path: ./src
target: /app/src
- action: rebuild
path: package-lock.jsonExecute:
docker compose watchSync reduz problemas de performance de bind mounts em alguns sistemas. Confirme as ações suportadas pela versão instalada.
Variáveis de ambiente
environment:
NODE_ENV: development
PORT: 3000Ou:
env_file:
- .env.composeNão versiona arquivos com secrets. Entenda a precedência entre shell, .env, env_file e environment.
Veja Variáveis de Ambiente no Node.js.
Interpolação
ports:
- "${APP_PORT:-3000}:3000"Para exigir uma variável:
image: "registry.example.com/app:${IMAGE_TAG:?IMAGE_TAG obrigatório}"Use:
docker compose configO comando mostra a configuração resolvida. Cuidado: ele pode exibir valores sensíveis.
Secrets
services:
api:
secrets:
- database_password
secrets:
database_password:
file: ./secrets/database_password.txtDentro do container, o secret fica normalmente em:
/run/secrets/database_passwordCompose local monta arquivos; isso não oferece automaticamente as mesmas garantias de Docker Swarm ou um secret manager. Proteja o arquivo e não o versione.
Consulte Gestão de Segredos no Node.js.
Healthcheck da API
healthcheck:
test:
- CMD
- node
- -e
- |
fetch('http://127.0.0.1:3000/health/ready')
.then(r => process.exit(r.ok ? 0 : 1))
.catch(() => process.exit(1))
interval: 10s
timeout: 3s
retries: 5
start_period: 20sVeja Docker Healthcheck no Node.js.
depends_on não é readiness por padrão
A forma curta controla ordem de início, não espera que o serviço esteja pronto:
depends_on:
- postgresUse condition com healthcheck:
depends_on:
postgres:
condition: service_healthyA aplicação ainda precisa tolerar desconexão e reconectar. O banco pode cair depois do startup.
Migrations
Crie um serviço one-off:
services:
migrate:
build:
context: .
target: build
command: npm run migrate
environment:
DATABASE_URL: postgresql://app:app@postgres:5432/app
depends_on:
postgres:
condition: service_healthy
profiles: ["tools"]Execute:
docker compose --profile tools run --rm migrateConsulte Migrações de Banco no Node.js.
Profiles
services:
mailpit:
image: axllent/mailpit
ports:
- "8025:8025"
profiles: ["dev-tools"]docker compose --profile dev-tools upProfiles mantêm ferramentas opcionais fora do startup padrão.
Workers
services:
worker:
build:
context: .
target: development
command: npm run worker:dev
environment:
DATABASE_URL: postgresql://app:app@postgres:5432/app
REDIS_URL: redis://redis:6379
depends_on:
postgres:
condition: service_healthy
redis:
condition: service_healthyAPI e worker usam a mesma imagem, mas comandos diferentes.
Escalando localmente
docker compose up --scale worker=3Não publique uma porta fixa do host para cada réplica. O broker ou banco deve distribuir o trabalho com idempotência.
Networks
services:
api:
networks: [frontend, backend]
postgres:
networks: [backend]
nginx:
networks: [frontend]
networks:
frontend:
backend:
internal: trueA rede interna evita acesso direto ao banco por serviços externos à rede.
Volumes nomeados
volumes:
postgres_data:Liste:
docker volume lsRemova containers sem apagar dados:
docker compose downRemova também volumes:
docker compose down -vUse -v somente quando realmente deseja perder o banco local.
Backup local
docker compose exec -T postgres \
pg_dump -U app -d app \
> backup.sqlRestore:
docker compose exec -T postgres \
psql -U app -d app \
< backup.sqlLogs
docker compose logs -f api workerA aplicação deve escrever logs estruturados em stdout e stderr. Não grave arquivos sem rotação dentro do container.
Comandos úteis
docker compose ps
docker compose top
docker compose images
docker compose exec api sh
docker compose run --rm api npm test
docker compose restart api
docker compose stop
docker compose downMúltiplos arquivos
Base:
docker compose -f compose.yaml -f compose.dev.yaml upProdução:
docker compose -f compose.yaml -f compose.prod.yaml configRevise as regras de merge. Arrays e mappings podem ser combinados de formas diferentes.
Override automático
compose.override.yaml é carregado automaticamente com o arquivo padrão. Use para desenvolvimento local e mantenha a base segura.
Configuração de produção
services:
api:
build:
target: production
command: node --enable-source-maps dist/server.js
restart: unless-stopped
read_only: true
tmpfs:
- /tmp
security_opt:
- no-new-privileges:trueCompose pode ser usado em host único, mas não oferece todas as capacidades de um orquestrador. Avalie rolling updates, secrets, autoscaling, scheduling e alta disponibilidade.
Usuário não-root
Defina USER node no Dockerfile. Se montar volumes, confirme permissões. Não execute como root apenas para contornar erros de escrita.
Filesystem read-only
read_only: true
tmpfs:
- /tmpAdicione volumes somente nos caminhos que realmente precisam ser graváveis.
Capabilities
cap_drop:
- ALLAdicione uma capability somente se a aplicação necessita. APIs Node.js comuns não precisam de privilégios Linux extras.
Docker socket
Evite montar:
/var/run/docker.sock:/var/run/docker.sockAcesso ao socket equivale a controle elevado do host em muitos cenários.
Limites de recursos
services:
api:
mem_limit: 512m
cpus: 1.0A sintaxe e suporte variam conforme o modo. Teste e monitore OOM. Ajuste o heap do Node.js com margem para Buffers e memória nativa.
Graceful shutdown
Compose envia SIGTERM antes de SIGKILL. Configure:
stop_grace_period: 30sA aplicação deve parar de aceitar tráfego, concluir trabalho, fechar conexões e sair. Veja Graceful Shutdown no Node.js.
Init
init: trueUm pequeno processo init encaminha sinais e coleta processos filhos. É útil quando a aplicação inicia child processes.
Testes de integração
docker compose -f compose.test.yaml up \
--build \
--abort-on-container-exit \
--exit-code-from testsGaranta cleanup:
docker compose -f compose.test.yaml down -v --remove-orphansCompose no CI
- run: docker compose -f compose.test.yaml config
- run: docker compose -f compose.test.yaml build
- run: docker compose -f compose.test.yaml up --abort-on-container-exit --exit-code-from tests
- if: always()
run: docker compose -f compose.test.yaml down -v --remove-orphansConsulte CI para Node.js com GitHub Actions.
Fixe versões e digests
Evite depender apenas de:
image: postgres:latestPrefira uma versão suportada e, em ambientes rigorosos, digest imutável. Atualize em pull requests testados.
Build secrets
Não passe tokens com ARG ou ENV durante build, pois podem aparecer em camadas. Use BuildKit secrets no Dockerfile e configuração de build correspondente.
Ambientes efêmeros
Defina project name diferente:
docker compose -p pr-123 up -dIsso isola nomes de containers, networks e volumes. Remova tudo ao final.
Orphans
Quando um serviço é removido do YAML:
docker compose up --remove-orphansRevise antes em ambientes com dados persistentes.
Configuração final validada
docker compose config --quietInclua no CI para detectar YAML inválido e variáveis ausentes.
Erros comuns
- localhost entre serviços: conexão aponta para o container errado.
- depends_on sem healthcheck: API inicia antes do banco.
- Portas desnecessárias: banco fica exposto.
- .env versionado: secrets vazam.
- Bind mount em produção: artefato não é imutável.
- latest: atualização inesperada.
- down -v sem cuidado: dados são apagados.
- Aplicação sem SIGTERM: requisições são interrompidas.
Conclusão
O Docker Compose para Node.js reúne aplicação, banco, cache e workers em uma configuração reproduzível. Services, networks, volumes, healthchecks e profiles tornam desenvolvimento e testes mais consistentes.
Use imagens multi-stage, dependências saudáveis, secrets protegidos, portas mínimas e shutdown gracioso. Separe overrides de desenvolvimento da configuração de produção e valide o YAML no CI. Assim, Compose simplifica o ambiente sem esconder requisitos de segurança, persistência e disponibilidade.



