↓Pular para o conteúdo principal
  1. blog/
ARTIGO TÉCNICO

Como adicionar apenas um novo serviço a um Compose em produção com docker compose up -d --no-deps

Por que, em host compartilhado, validar o Compose e subir somente o serviço novo com --no-deps reduz o blast radius da mudança.

·5 minutos

Um docker compose up -d parece inofensivo quando a mudança no YAML é pequena.

O problema é que o Compose não lê intenção. Ele lê o projeto inteiro.

Em uma migração real de APIs para hosts Docker já usados por outros serviços, a regra operacional ficou explícita:

se a mudança é um serviço novo, subir só aquele serviço.

O fluxo usado era próximo de:

1
2
3
4
5
docker compose config --services
docker compose config --images
git diff
docker compose pull <servico>
docker compose up -d --no-deps <servico>

O objetivo não era fazer uma “mágica sem downtime”.

Era algo mais básico e importante: não envolver aplicações que não faziam parte da mudança.

Antes do up, validar o arquivo #

A primeira etapa não era subir nada.

Era pedir para o Compose interpretar o YAML:

1
docker compose config --services

Depois conferir as imagens:

1
docker compose config --images

Isso pega uma classe inteira de problemas antes de qualquer alteração no runtime:

  • YAML inválido;
  • variável não resolvida;
  • nome de serviço errado;
  • imagem inesperada;
  • referência de configuração incorreta.

Em ambiente versionado, o próximo passo é quase obrigatório:

1
git diff

Se eu não consigo explicar o diff antes do deploy, ainda não estou pronto para aplicar o diff.

Por que não usar docker compose up -d geral #

Imagine um host com vários serviços existentes:

1
2
3
4
5
6
traefik
api-a
api-b
worker
redis
serviço-novo

O trabalho atual é adicionar serviço-novo.

Rodar:

1
docker compose up -d

entrega ao Compose o projeto inteiro para reconciliação.

Mesmo quando ele não recria tudo, você aumentou desnecessariamente o escopo da operação e ficou dependente do estado de todos os componentes definidos naquele arquivo.

Na migração que originou este artigo, isso era um risco sem benefício.

Então a execução foi limitada:

1
docker compose up -d --no-deps serviço-novo

O que --no-deps significa aqui #

O parâmetro instrui o Compose a não iniciar serviços declarados como dependências daquele serviço por causa dessa operação.

Isso faz sentido quando:

  • as dependências necessárias já estão ativas;
  • a mudança não pretende reconciliá-las;
  • o operador validou essa condição antes;
  • o objetivo é limitar a alteração ao serviço especificado.

Não é uma regra universal para todo deploy.

Se o novo serviço depende de uma dependência que ainda precisa ser criada, --no-deps pode justamente impedir o comportamento esperado.

O valor está no uso consciente, não no parâmetro em si.

Pull também deve ser específico #

O mesmo raciocínio vale para imagem:

1
docker compose pull serviço-novo

Antes de subir, eu quero saber se o host realmente consegue baixar aquela imagem.

Se o pull falha por registry, IAM ou autenticação, melhor descobrir nesse comando do que misturado com a criação do container.

Esse isolamento melhora o diagnóstico:

1
2
3
4
pull falhou     → registry / rede / autenticação / imagem
up falhou       → compose / runtime / mounts / rede / config
health falhou   → aplicação / dependência / configuração
proxy falhou    → roteamento / labels / porta / TLS

Depois do up, não parar no docker ps #

Container Up não é aplicação validada.

A sequência usada na migração validava várias camadas.

Primeiro:

1
2
docker ps --filter name=serviço-novo
docker logs --tail=100 serviço-novo

Depois, quando havia health endpoint, testar diretamente o container:

1
2
IP="$(docker inspect -f '{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}' serviço-novo)"
curl -i "http://${IP}:8080/health"

Só depois seguir para reverse proxy, origin externo e CDN/HMG.

Essa ordem evita começar a investigação no componente mais distante do problema.

O benefício real é blast radius #

A pergunta que guia esse tipo de deploy é:

quantos componentes eu preciso colocar em risco para fazer esta mudança?

Se a resposta é “um”, executar um comando que engloba cinco serviços é um desenho operacional pior.

Na prática, a mudança ficou parecida com:

1
2
3
4
5
6
7
editar Compose
→ validar Compose
→ revisar diff
→ pull do serviço
→ subir só o serviço
→ validar container
→ validar publicação

Cada etapa responde uma pergunta antes de avançar para a seguinte.

Quando eu não usaria esse padrão #

Eu não usaria --no-deps automaticamente quando:

  • a dependência também faz parte da mudança;
  • uma nova rede/serviço dependente precisa ser criada;
  • houve alteração conjunta de versões entre componentes acoplados;
  • o estado atual das dependências é desconhecido;
  • a própria mudança exige recriação coordenada.

Nesse caso, o problema não é o comando mais amplo. É fingir que a mudança é menor do que realmente é.

Checklist curto #

Para adicionar um único serviço em um Compose que já atende outras aplicações:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
[ ] salvar/versionar o estado atual
[ ] editar apenas o necessário
[ ] docker compose config --services
[ ] docker compose config --images
[ ] revisar git diff
[ ] docker compose pull <serviço>
[ ] docker compose up -d --no-deps <serviço>
[ ] docker ps
[ ] docker logs
[ ] health direto no container
[ ] teste no proxy/origin
[ ] registrar resultado

O que ficou desse caso #

O comando mais perigoso nem sempre é o que parece destrutivo.

Às vezes é só um comando amplo demais para uma mudança pequena.

docker compose up -d --no-deps <serviço> funcionou como guard-rail porque o contexto já estava validado e o objetivo era adicionar uma API em paralelo sem pedir ao Compose que tocasse no restante do host.

A ideia que vale reaproveitar é mais geral:

se a mudança tem blast radius de um serviço, tente manter a execução com blast radius de um serviço também.

TEM UM CENÁRIO PARECIDO?

Me chama.

Manda o contexto, os sintomas e o que já foi testado. Bora organizar as evidências antes de sair mexendo.

Falar com Castro →
Sem enrolação. Com evidência.