Dois deploys em producao da mesma branch

Voce tem um repositorio (ex.: um miniSMTP) e precisa rodar dois deploys em producao com configuracoes diferentes — dominios, HELO, credenciais de relay. Mesmo codigo, mesma branch, configs distintas.

Feito do jeito errado, um deploy sobrescreve o outro. Este guia mostra o jeito que nao quebra.

O que quebra (e por que)

Se voce simplesmente registra a mesma branch duas vezes:

  • o .deploy.yml e um so (esta na branch compartilhada) — colocar os dois configs literais nele faz um sobrescrever o outro;
  • o bundle cifrado tem um caminho no repo — dois registros escrevendo no mesmo nome se clobberam;
  • runner add bloqueia o segundo registro (exige --force-duplicate);
  • e ha uma nuance de qual instancia o deploy escolhe — resolvida na v2.55.1 (veja Qual instancia o deploy escolhe).

O anti-pattern e por o valor especifico de cada deploy dentro do manifesto. A branch e compartilhada; o manifesto so pode carregar o formato.

O principio

.deploy.yml   →  SHAPE: nome da instancia, {{::REFERENCIAS}}, literais COMUNS
secret.enc    →  VALORES de cada deploy (namespaced por instancia)

Detalhes em Hierarquia env/secrets.

Saida recomendada: um manifesto por deploy

Cada deploy ganha seu proprio manifesto dentro do mesmo repo. Assim cada registro tem uma unica instancia — sem ambiguidade de "qual instancia deployar" — alem de app dir, ckey e bundle proprios.

1. Crie um manifesto por deploy no repo

seu-repo/
├── src/
├── deploy/
│   ├── cliente-a/.deploy.yml
│   └── cliente-b/.deploy.yml

deploy/cliente-a/.deploy.yml:

project: smtp-cliente-a
system: sys
type: sys
image: python:3.12-slim
port: 2525

github:
  repo: usuario/smtp-mini
  dist_branch: main

version:
  source: git-commit

environment:
  SMTP_MAX_SIZE: "26214400"        # literal COMUM aos dois
  SMTP_HELO: mx.clientea.com       # literal DESTE deploy
secrets:
  SMTP_RELAY_PASSWORD: "{{::SMTP_RELAY_PASSWORD}}"   # referencia; valor no bundle

healthcheck:
  tcp: 2525

instances:
  cliente-a:                        # UMA instancia por manifesto
    domain: mx.clientea.com
    source:
      type: dist
    keep_versions: 3

deploy/cliente-b/.deploy.yml e igual, trocando project, SMTP_HELO, o nome da instancia (cliente-b) e o dominio.

Como cada manifesto tem uma unica instancia, ela e o default — e tanto runner deploy quanto fetch --deploy acertam sozinhos.

2. Registre os dois (cada um com sua ckey)

runner add --repo usuario/smtp-mini --branch main \
  --token "$(gh auth token)" \
  --manifest-path deploy/cliente-a/.deploy.yml \
  --slug smtp-cliente-a \
  --instance cliente-a \
  --ckey "<ckey-do-cliente-a>"

runner add --repo usuario/smtp-mini --branch main \
  --token "$(gh auth token)" \
  --manifest-path deploy/cliente-b/.deploy.yml \
  --slug smtp-cliente-b \
  --instance cliente-b \
  --ckey "<ckey-do-cliente-b>"
Flag Papel
--manifest-path aponta o manifesto daquele deploy (implica --force-duplicate quando ja existe outro app do mesmo repo)
--slug nome/diretorio proprio do registro (/data/apps/smtp-cliente-a) — isola .env/.secrets/state
--instance fixa a instancia do registro e namespaceia o bundle (secrets.cliente-a.enc)
--ckey chave propria — o bundle de A nunca abre com a ckey de B

3. Preencha os valores sensiveis (por registro)

runner env set --secret smtp-cliente-a SMTP_RELAY_PASSWORD 'senha-do-A'
runner env set --secret smtp-cliente-b SMTP_RELAY_PASSWORD 'senha-do-B'

Valor com caractere especial vai sempre aqui (o store e verbatim), nunca no manifesto — as aspas sao do seu shell.

4. Deploy

runner deploy smtp-cliente-a
runner deploy smtp-cliente-b

Cada um sobe com seu dominio, seu env e seus secrets. No repo, os bundles ficam separados:

.runner/
├── secrets.cliente-a.enc     # so abre com a ckey de A
└── secrets.cliente-b.enc     # so abre com a ckey de B

Alternativa: manifesto unico com `instances:`

Se a config que diverge e apenas nao-sensivel (dominio, HELO, portas) e os secrets sao os mesmos, da pra usar um manifesto so com duas instancias:

instances:
  cliente-a:
    domain: mx.clientea.com
    source: { type: dist }
    environment_overrides:            # prioridade maxima
      SMTP_HELO: mx.clientea.com
  cliente-b:
    domain: mx.clienteb.com
    source: { type: dist }
    environment_overrides:
      SMTP_HELO: mx.clienteb.com

Limites desta alternativa:

  • Uma ckey por registro. state.ckey e um campo unico por app registrado — duas instancias no mesmo registro compartilham ckey e .env/.secrets. Se os secrets divergem, use a saida recomendada.
  • Rodar runner set-ckey com uma ckey diferente da ja registrada retorna erro loud (exit 1) sem escrever nada.

Qual instancia o deploy escolhe (v2.55.1+)

A partir da v2.55.1, runner deploy, runner deploy --all e runner fetch --deploy resolvem a instancia da mesma forma: state.instance (a instancia com que o app foi registrado via runner add --instance <N>) → default do manifesto → production.

Ou seja, num manifesto com duas instancias, cada registro deploya a sua instancia registrada — tanto no manual quanto no cron.

runner deploy smtp-cliente-b         # honra state.instance = cliente-b
runner fetch --deploy smtp-cliente-b # idem

Em runners anteriores a v2.55.1, os deploys manuais (deploy/--all) ignoravam state.instance e miravam sempre a instancia default do manifesto — so o fetch --deploy respeitava o registro. Se voce esta numa versao antiga, atualize (runner self-update) ou registre um manifesto por deploy (a saida recomendada, que elimina a ambiguidade em qualquer versao).

Verificacao

runner list                              # os dois registros, dominios distintos
runner manifest smtp-cliente-a           # manifesto resolvido daquele deploy
runner secrets show smtp-cliente-a       # abre so com a ckey de A
runner status smtp-cliente-b

Armadilhas

Nao faca Por que
Dois blocos de config literais no mesmo manifesto a branch e compartilhada — um sobrescreve o outro
Mesma ckey nos dois deploys vazou uma, abriu as duas (perde o blast-radius)
Mesmo --slug (ou sem slug) os registros colidem no mesmo app dir
Sem --instance os bundles colidem no mesmo nome no repo
Secret com caractere especial no manifesto o YAML reinterpreta $, {}, # — use env set --secret

Veja tambem

By Borlot.com.br on 23/07/2026