Nome da app e project (2.185.0+)

Desde a 2.185.0 o runner separa duas coisas que antes eram uma só:

De onde vem Para que serve
nome_base (identidade da app) O state da app. App nova: o nome do diretório (/data/apps/<app>). App já registrada: o nome que ela já usava, congelado na migração Nome do container, do router e do service do Traefik, dos arquivos de rota, da imagem buildada, do canary e dos signals
project (agrupador) O project: do .deploy.yml. Sem ele, o nome do repositório e, por fim, o diretório Label ccs.systems/project, RUNNER_PROJECT, notificações e agrupamento no CCS

O project pode repetir. Duas apps com o mesmo project: na mesma instância sobem lado a lado, e o deploy, o rollback, o cleanup e o unregister de uma não tocam na outra. É o caso de um produto com várias partes: loja-front, loja-api e loja-worker, todas com project: loja.

A segunda app do mesmo project: precisa de --slug no registro. O runner add cria o diretório da app pelo project: do manifesto, então a primeira app com project: loja fica em /data/apps/loja, e a segunda, sem --slug, cai no mesmo diretório e o registro falha com App already registered. Dê a cada parte o seu nome com --slug:

runner add --repo exemplo/loja --ckey v2                      # /data/apps/loja
runner add --repo exemplo/loja-api --slug loja-api --ckey v2  # /data/apps/loja-api

As duas continuam com project: loja (o agrupador), e cada uma tem o seu nome_base (loja e loja-api).

O runner anuncia esse comportamento com a capability app.nome_base.v1:

runner --capabilities

De onde sai cada nome

Com nome_base = nb e a instância inst:

O quê Nome
Container {nb}_{inst}_{versão}, com - virando _ (loja-api → loja_api_production_v1-2-0)
Router e service do Traefik {nb}-{inst}
Arquivo de rota (modo file) {dono}_{nb}__{inst}.yml, com o marcador instance-id: {nb}/{inst}
Gatekeeper runner-gate--{nb}--{inst}.yml e {nb}__{inst}__tls.yml
Imagem buildada {nb}:{versão}
Canary e signals pela chave {nb}

O diretório, o state, os locks, src/, data/ e logs/ continuam pelo nome do diretório, como antes. Volumes nomeados e redes são os literais do manifesto.

Labels e variáveis do container

Label / variável Valor
ccs.systems/uid O diretório da app. É a chave para achar os containers de uma app
ccs.systems/nome-base O nome_base (novo na 2.185.0, para leitura e diagnóstico)
ccs.systems/project O agrupador (o project: do manifesto). Pode repetir entre apps
RUNNER_APP O diretório da app
RUNNER_PROJECT O agrupador

Para achar os containers de uma app, filtre pelo uid, nunca pelo project:

docker ps --filter label=ccs.systems/uid=loja-api --format '{{.Names}}'

Filtrar por ccs.systems/project=loja traz as três partes da loja.

Apps já registradas: nada é renomeado

Na primeira vez que o deploy, o rollback, o fetch ou o manifest pin leem o state de uma app sem nome_base, o runner grava o nome que ela usa de fato:

  1. com o Docker respondendo, o nome do container em execução da app (pelo ccs.systems/uid), na instância registrada;
  2. com o Docker respondendo e nenhum container da app no ar: o project de uma troca da 2.184.0 ainda não deployada, o --slug do registro, o project: do manifesto e, por fim, o diretório.

Se o Docker não responder naquele momento, o runner calcula o nome pela regra do item 2 e não grava: a migração fica para a próxima vez em que o Docker responder, para decidir pelo container que está no ar.

No primeiro deploy com a 2.185.0, o container sobe com o mesmo nome base e a rota continua no mesmo arquivo e com o mesmo router. Só o label ccs.systems/project pode mudar, e só nas apps em que ele era diferente do project: do manifesto:

  • a app registrada com --slug: a loja-api (project: loja, --slug loja-api) continua com o container loja_api_production_… e a rota loja-api-production, e o label passa de loja-api para loja;
  • a app que trocou de project: depois do registro.

App adotada (o diretório loja_exemplo_com_br com project: loja-sys) e pasta criada à mão ficam com o prefixo atual congelado. Um runner anterior ignora o campo novo do state e continua montando os mesmos nomes.

Trocar o `project`

Trocar o project: do .deploy.yml muda só o agrupamento: no próximo deploy, só o label ccs.systems/project muda. Container, rota e imagem ficam iguais, sem limpeza nem janela de rota duplicada.

O manifest pin ainda pede confirmação, porque o agrupamento no CCS muda:

runner manifest pin loja-api --base-sha256 <sha> --aceita-avisos projeto_alterado < novo.deploy.yml

O aviso projeto_alterado (amarelo) diz que nada é renomeado. O vermelho project_de_outra_app_na_instancia da 2.184.0 deixou de existir: repetir o project de outra app é o caso normal.

A única colisão: `nome_base_de_outra_app`

Duas apps não podem ter o mesmo nome_base na mesma instância, porque os nomes de container e de rota seriam os mesmos. O runner compara pelo nome do container, então lo-ja colide com lo_ja. O vermelho é nome_base_de_outra_app:

Onde Comportamento
runner add (com ou sem repositório) Recusado antes de criar diretório ou state
manifest pin / manifest validate Componente identidade, caminho /project, dados.nome_base e dados.apps. Exit 14 no pin; o --aceita-avisos não libera
Deploy Recusado antes de qualquer efeito

Na prática ele só aparece em dois casos: --slug repetido, e o par degenerado de antes da 2.185.0 (duas apps sem slug, com o mesmo project:, registradas na mesma instância, que já derrubavam uma à outra). O deploy do par degenerado passa a ser recusado. Para sair, registre uma das apps de novo com outro --slug.

Enquanto o par existir, o unregister de uma delas preserva, com aviso, o que só se reconhece pelo nome e pode ser da outra: o arquivo de rota, o gatekeeper e o container legado sem uid dessa instância. Os containers com o uid da app removida saem.

Onde ver o `nome_base`

runner status loja-api --json        # "nome_base" ao lado de "app"
runner instances loja-api --json     # cada item traz "nome_base"
runner snapshot --json               # projects[].nome_base e instances[].nome_base
runner manifest context loja-api     # apps[].nome_base; apps[].projeto é o agrupador

Sem nome_base no state (app ainda não migrada), esses comandos mostram o valor que a migração gravaria, sem gravar nada.

Veja também

By Borlot.com.br on 03/11/2026