Deploy — control-plane no central-backend¶
Status: Aprovado · Responsável: Gustavo Madruga · Atualizado em: 2026-09-12
Aplica-se a: perfis app e config com recurso no Coolify.
Como o CI dispara o deploy depois do build off-host. A norma está em Entrega e o porquê na 0026; esta página é o contrato operável.
Modos de deploy (por tipo de projeto)¶
| Tipo de projeto | Recurso Coolify | Quem builda | Gatilho |
|---|---|---|---|
| Server Micronaut | native-pull (padrão); jar-pull quando jar é declarado com motivo |
job build_native/build_jar do pipeline.yml |
control-plane, target=native/jar |
| Flutter web | web-pull |
job build_web |
control-plane, target=web |
Stack compose (perfil config) |
compose do git | o Coolify, a partir do git | control-plane, target=compose |
| Site de docs do central | build do Dockerfile |
o Coolify, a partir do git | auto-deploy no push |
sulplata-ho-scraper (Node) |
build do Dockerfile |
o Coolify, a partir do git | auto-deploy no push |
vantroba-powersync |
compose do git | o Coolify, a partir do git | auto-deploy no push (e control-plane) |
As três últimas linhas são os recursos que ainda seguem em auto-deploy: dívida do central, com a migração para o control-plane no backlog. Todo o resto dispara pelo control-plane, com o gate verde.
O vantroba-powersync é o caso a corrigir primeiro: é a única das seis stacks PowerSync com
auto-deploy ligado, e o gatilho do Coolify não conhece o filtro de caminho do pipeline-config.yml —
qualquer push no repo, inclusive de doc, reinicia a stack.
O fluxo¶
flowchart TB
CI["job deploy do pipeline.yml"] -- "POST /api/ci/deploy<br/>Bearer CENTRAL_DEPLOY_TOKEN" --> C[central-backend]
C -- "resolve o recurso<br/>app_deploy_targets" --> C
C -- "POST /deploy?uuid=" --> K[API do Coolify]
K -- "rolling: novo sobe, health, velho drena" --> A[Container do app]
O contrato¶
POST https://central-backend.xadm.biz/api/ci/deploy
Authorization: Bearer <CENTRAL_DEPLOY_TOKEN>
Content-Type: application/json
# recurso que puxa imagem (jar/native/web)
{ "image": "fonte.xadm.biz/xadm/<slug>:<target>-amd64", "target": "<target>" }
# recurso compose que o Coolify builda do git
{ "app_id": "<slug do repo git>", "target": "compose" }
→ 200 { "deployments": [ { "coolify_uuid": "8wih…", "deployment_uuid": "aavd…", "status": "queued" } ] }
- Auth: token dedicado
CENTRAL_DEPLOY_TOKEN, validado por um filtro próprio em/api/ci/**. - Âncora: com
image, o slug da imagem; semimage, oapp_idé o slug e otargeté obrigatório (400 missing_anchor/400 missing_target). target:jar|native|web|compose.imageé rastro: o recurso tem tag fixa (<target>-amd64) e o deploy re-puxa essa tag; o central não troca a imagem do recurso.- Resposta assíncrona:
queuedassim que o Coolify enfileira; o Coolify faz o rolling. Quem verifica produção é o smoke. - Endpoint do Coolify:
POST {coolify}/deploy?uuid={uuid}&force=false— não o/restart, que só reinicia a mesma imagem. - URL única, flavor-agnóstica: o
CENTRAL_DEPLOY_URLapontacentral-backend.xadm.biz, nunca o domínio de um flavor de build ou de uma instância. O aviso de publish das docs deriva dele:CENTRAL_DOCS_URL="${CENTRAL_DEPLOY_URL%/deploy}/docs-published". - Rollback automático: pendente no control-plane. O endpoint aceita só a tag móvel
(
…:<target>-amd64); a tag imutável<sha>-<target>volta400 invalid_image. Até o control-plane aceitar a imutável e trocar a tag do recurso antes do deploy, o smoke reprova e avisa, e o operador reverte apontando<sha>-<target>no recurso e fazendo Redeploy.
O mapa — app_deploy_targets¶
O mapa vive no banco do central-backend, sem chave estrangeira para o catálogo de apps. Desde a
0038 o app_id e o slug da imagem são o mesmo valor, mas
a FK continua fora por outro motivo: nem todo recurso deployável tem linha em apps — as stacks
compose (PowerSync, uma por cliente) são ancoradas pelo slug do repo git e não são apps do catálogo.
Uma FK aqui impediria de mapeá-las:
| coluna | |
|---|---|
registry_slug |
slug do artefato que o recurso consome — a chave do reconcile: o slug da imagem (0024) ou, no compose, o slug do repo git |
target |
jar | native | web | compose |
instance |
instância, derivada do FQDN int.<cli>.xadm.biz ou int-jar.<cli>.xadm.biz (o jar da troca); vazia no app de uma instância só |
coolify_uuid |
uuid do recurso Coolify, gravado pelo reconcile |
fqdn |
domínio do recurso |
Unicidade por (registry_slug, target, coalesce(instance, '')). Cada disparo grava uma linha em
deploy_audit (slug, alvo, instância, recurso, deployment, resultado e uma referência não reversível ao
token).
Formatos de recurso, na fórmula <slug>-[<instance>-]<target>-pull:
- um recurso — o Flutter web (
<slug>-web-pull); - dois recursos no mesmo domínio — native e jar do mesmo app; o
targetdesambigua (coolify); - multi-instância — o
integrador-server, um recurso por cliente e alvo (integrador-server-<cli>-<target>-pull).
(registry_slug, target) sem instance seleciona todas as linhas que casam: um release do integrador
deploya todas as instâncias; instance presente restringe a uma.
Reconcile — por que o mapa não envelhece¶
Recurso recriado (blue-green) ganha uuid novo, então a chave de casamento nunca é o uuid nem o nome do recurso. O reconcile lista os recursos do Coolify e casa:
- recurso que puxa imagem pelo
docker_registry_image_name(último segmento =registry_slug); - recurso compose (
build_pack=dockercompose) pelo slug do repositório git (último segmento dogit_repository, sem.gite sem credencial):…/onpetro-powersync.git→onpetro-powersync,target=compose. Recurso compose sem repositório git é pulado;build_packfora de{dockerimage, dockercompose}é ignorado.
Renomear o recurso não exige ação. Recurso cujo slug não casa com nada é logado como órfão; o reconcile
nunca apaga linha nem inventa app. Roda no boot, sob demanda e quando a resolução não acha o recurso.
Assunção: um repo git = uma stack compose; a segunda stack do mesmo repo precisa de instance.
O central se deploya pelo próprio endpoint¶
O job deploy do pipeline.yml do central-backend chama o endpoint público do próprio central, que
relaya à API do Coolify de dentro da casa. É seguro: o central atual está no ar no disparo, e o Coolify
enfileira e só troca o container quando o novo fica saudável.
Central fora do ar: o endpoint não responde e o deploy é à mão, de dentro da casa — Redeploy do
recurso na UI do Coolify (apontando a tag <sha>-<target> anterior, se a nova é a quebrada), ou
POST /api/v1/deploy?uuid=<uuid> na API do Coolify.
Roteiro de adoção¶
- Recurso Coolify pull-only (ou compose do git) com auto-deploy desligado.
- No repo, no GitHub: secret
CENTRAL_DEPLOY_TOKENe variávelCENTRAL_DEPLOY_URL(https://central-backend.xadm.biz/api/ci/deploy). - Reconcile no central (sob demanda) e um deploy de teste.
- Apagar do GitHub
DEPLOY_SSH_KEY,COOLIFY_TOKENeCOOLIFY_NATIVE_UUIDe revogar esse token no Coolify — o gatilho por SSH não existe mais.