Hierarquia de fonte-da-verdade (env/secrets)
Um valor de ambiente pode existir em mais de um lugar: no .deploy.yml, no .env/.secrets local e no bundle encriptado .runner/secrets.enc. Esta pagina define quem vence quando o mesmo KEY aparece em mais de um, e como isso evita que um valor real seja sombreado por vazio.
O contrato
secret.enc > env/.secrets (local) > deploy.yml
(bundle) (valor do operador) (defaults do manifesto).runner/secrets.enc(bundle) — a fonte autoritativa e recuperavel. Um valor real no bundle nunca e sombreado por uma linha local vazia..env/.secrets(local) — o valor efetivo em runtime; e o que oenv setgrava. Um valor real aqui vence um bundle desatualizado (o operador acabou de setar)..deploy.yml— o piso: so fornece o que ele mesmo possui (literais e geradores). Nunca escreve por cima das camadas de cima.
A partir da v2.55.0 esse contrato e garantido no codigo:
{{::}}que resolve vazio nao vira linhaKEY=, e o merge do bundle nao deixa vazio local sombrear valor real.
Os tres papeis do manifesto
Dentro de environment: / secrets: do .deploy.yml convivem tres papeis. Saber qual e qual evita o erro classico de "setei e nao pegou":
| Sintaxe | Papel | O manifesto e dono? | Efeito |
|---|---|---|---|
KEY: "valor" |
valor literal (config, default) | Sim | escreve — piso (so se a chave falta) |
KEY: "{RANDOM}" / "${GENERATE:hex:32}" |
gerador | Sim (gera uma vez) | escreve o valor gerado |
KEY: "{{::KEY}}" |
referencia — dono e env/secret.enc | Nao | nunca escreve; interativo pergunta, nao-interativo e no-op |
O ponto-chave: {{::KEY}} nao e um valor, e um ponteiro dizendo "esse valor vem de fora". Num deploy nao-interativo (CI, fetch --deploy) ele nao tem como perguntar, entao nao materializa nada — a chave fica ausente e o valor real (do .secrets/bundle) e quem manda.
Um literal vazio proposital (KEY: "", sem {{::) continua sendo escrito — vazio intencional e respeitado.
Por que "setei e nao pegou" acontecia (< v2.55.0)
Antes da v2.55.0, um {{::KEY}} num deploy nao-interativo resolvia para "" e materializava KEY= no .env/.secrets. Essa linha vazia era pegajosa: o merge nunca a sobrescrevia e a hidratacao do bundle preferia o local — entao o valor real (no secrets.enc) ficava permanentemente sombreado por vazio. Sintoma tipico: app sobe com DATABASE_URL="" e o Hibernate/driver falha na conexao mesmo com o valor "setado".
A correcao (v2.55.0) tem dois lados, espelhando o contrato:
deploy.ymlnunca envenena:{{::}}que resolve vazio e descartado, nao vira linha vazia.secret.encvence o vazio: no merge do bundle, uma linha local vazia cede ao valor nao-vazio do bundle.
Onde por cada valor
| Tipo de valor | Onde declarar | Por que |
|---|---|---|
| Config / default nao-sensivel | environment: no .deploy.yml (literal) |
o manifesto e dono; e o piso |
| Segredo (senha, token, chave, URL com credencial) | store, via runner env set --secret |
verbatim, encriptado no bundle, recuperavel |
| Valor que vem do operador/CCS por deploy | {{::KEY}} no manifesto + valor no store |
manifesto so referencia |
| Gerado uma vez | {RANDOM} / ${GENERATE:hex:N} |
o runner gera e persiste |
Regra pratica: valor com caractere especial ou sensivel vai no store (env set --secret), nunca como literal no manifesto YAML.
Caracteres especiais
O store (.env/.secrets) e verbatim: o runner guarda e injeta o valor byte-a-byte. O manifesto YAML, ao contrario, reinterpreta varios tokens. Por isso valor com caractere especial pertence ao store.
| Caractere no valor | No store (env set) |
No manifesto (.deploy.yml) |
|---|---|---|
$ (p@$$word) |
verbatim — docker --env-file nao expande |
${...} e tratado como referencia compose (some) |
{ } |
verbatim | colide com {{::}} / {RANDOM} / ${GENERATE} |
# |
literal (comentario so no inicio da linha) | YAML: inicia comentario se nao citar |
= |
preservado (divide so no 1º =) |
ok, se citar no YAML |
espaco / : / * / ! / & |
verbatim (trailing e trimado) | precisa de aspas YAML |
aspas " ' |
literais (nao encapsulam) | aspas YAML (escape) |
| newline (PEM, JSON multi-linha) | nao suportado — use base64 ou mount de arquivo | idem |
Aspas sao do shell
Ao usar env set, as aspas sao do seu shell, nao do valor. O shell consome as aspas e entrega o literal ao runner:
# certo: o shell tira as aspas, o runner guarda: p@$$w{}rd#1
runner env set --secret meu-app DB_PASSWORD 'p@$$w{}rd#1'
# errado: aspas dentro do valor viram literais
runner env set --secret meu-app DB_PASSWORD '"senha"' # guarda: "senha" com aspasMulti-linha (PEM, JSON)
O formato --env-file do docker e por-linha; um valor nao pode conter \n. Para segredos multi-linha:
- base64 — encode no
env set, a app decodifica em runtime; ou - mount de arquivo — o segredo vira arquivo no container.
Veja tambem
- Classificacao automatica de env/secrets — como o runner decide env vs secret.
- Secrets lifecycle — bundle encriptado,
.env/.secrets, injecao no container. - Anatomia do .deploy.yml — schema completo do manifesto.