Secrets obrigatórios não resolvidos

O deploy para quando uma chave listada em required: não chega ao ambiente final do container. Isso é proposital: subir uma app sem credencial reportando sucesso é pior do que não subir.

A partir da v2.69.0 a mensagem diz onde o runner procurou o bundle.


O sintoma

secrets_not_materialized: 2 secret(s) obrigatório(s) em `required:`
(["DB_PASSWORD", "API_KEY"]) NÃO foram resolvidos — nenhum chegou ao ambiente
final do container (nem em runtime.env, nem em .env/.secrets).

Deploy interrompido: melhor parar do que subir uma app sem credencial
reportando verde.

  Um bundle de secrets existe, mas nenhum valor para as chaves acima veio dele.
  Verifique o conteúdo do que está marcado abaixo:
    - /data/apps/minha-app/.runner/secrets.production.tester.enc  (namespace do servidor (`target:` no config.yml))
    - /data/apps/minha-app/.runner/secrets.production.enc  (instância)  <- existe
    - /data/apps/minha-app/.runner/secrets.enc  (legado (bundle único))  <- existe
  Preencha:  runner env set <app> <KEY> <VALOR>   (use --secret para valor sensível)

Uma mensagem equivalente aparece como missing_required quando as chaves existem no ambiente porém vazias. Ambas listam os mesmos candidatos.

Como ler a lista

Os caminhos aparecem na ordem em que o runner os consulta, do mais específico para o mais genérico. Cada linha traz a origem entre parênteses, e <- existe marca os que estão no disco.

Origem Quando é usado
namespace do servidor target: no config.yml — separa bundles por servidor
instância Bundle por instância (production, staging, ...)
legado (bundle único) Formato antigo, um bundle para todas as instâncias

O cabeçalho diz o que foi verificado, e muda a conduta:

  • "Um bundle de secrets existe..." → o arquivo está lá; o problema é o conteúdo. Não perca tempo procurando arquivo.
  • "Nenhum bundle de secrets foi encontrado..." → nenhum candidato existe; o problema é o arquivo ausente.

Correção

Caso 1 — o bundle existe mas não tem a chave

O mais comum: a chave foi acrescentada ao required: e ninguém preencheu o valor.

runner env set minha-app DB_PASSWORD 'valor' --secret
runner deploy minha-app

Para conferir quais chaves já estão no bundle sem decifrar valores:

runner env list minha-app

Isso funciona em bundles de ckey assimétrica, onde os nomes ficam legíveis e só os valores são selados. Em bundle de ckey simétrica (o padrão atual) o arquivo é um blob opaco e não há como listar chaves sem a ckey — ver ckey simétrica e assimétrica.

Caso 2 — nenhum bundle existe

App nova, ou bundle perdido na migração de servidor. Preencher as chaves cria o bundle:

runner env set minha-app DB_PASSWORD 'valor' --secret
runner env set minha-app API_KEY 'valor' --secret

Se o bundle deveria existir e sumiu, veja Restaurar em novo servidor.

Caso 3 — o bundle existe mas não abre

Se a mensagem for de falha ao decifrar (e não de valor ausente), o problema é a ckey, não o preenchimento. Veja Bundle decrypt failed.

Caso 4 — a chave não é obrigatória de verdade

Se ela pode faltar legitimamente, tire-a de required:. Declarar em secrets: já a mantém cifrada sem exigir valor:

secrets:
  ANALYTICS_TOKEN: ""      # cifrado, opcional

instances:
  production:
    required:
      - DB_PASSWORD        # este sim bloqueia o deploy se faltar

Bypass pontual

Para um deploy meio-configurado, com consciência do risco:

runner deploy minha-app --allow-missing-required

Não use isso em produção como rotina. O gate existe porque app sem credencial costuma falhar mais tarde, de forma mais confusa.

Quando o gate NÃO dispara

Não há gate de secret sem required: declarado. Uma instância com required: [] substitui a lista da base por vazio e passa limpo — enquanto required: ausente herda a lista da base.

instances:
  staging:
    required: []           # nenhum obrigatório aqui
  production:
                           # ausente → herda o required: da base

Ver também

By Borlot.com.br on 06/08/2026