Secrets lifecycle
O Runner encripta secrets em duas camadas e os armazena no próprio repositório da app. Isso permite recuperação zero-touch em caso de perda do servidor. Esta página explica o ciclo completo — da definição no .deploy.yml até a injeção no container.
Pré-requisitos
- Runner instalado e inicializado (
runner initgera a master key) - App registrada com
runner add --ckey— ou, se já registrada sem ckey, anexe uma comrunner deploy <app> --ckey(ver Adicionar ckey a um app já registrado). Sem ckey, não há.secrets— tudo vai para.env.
O diagrama
DEPLOY.YML (manifest) LOCAL (servidor) REPO (git)
────────────────── ───────────────── ──────────
environment: .env (plaintext) (não versionado)
FLASK_ENV: production ──────▶ FLASK_ENV=production
BASE_DOMAIN: "{{::...}}"
.secrets (plaintext) (não versionado)
secrets: ──────▶ DB_PASSWORD=a8Kp2m...
DB_PASSWORD: "{RANDOM}"
JWT_SECRET: "{{::...}}" ↓ Phase 7 (push secrets)
encripta com ckey
(AES-256-GCM)
│
▼
.runner/secrets.enc ──▶ .runner/secrets.enc
(encriptado) (versionado no repo)
STATE (servidor)
────────────────
/opt/runner/state/<app>.yml
ckey: enc:nonce:cipher ◀── ckey encriptada com mkey
(mkey encripta ckey em repouso)
CONTAINER (runtime)
───────────────────
.env + .secrets injetados
via volume ou env varsAs duas chaves
mkey (master key)
Gerada pelo runner init, armazenada em /opt/runner/.keys/master.key (modo 600). Encripta as ckeys de todas as apps em repouso no state file. É efêmera — se o servidor morrer, uma nova mkey é gerada no runner init do novo servidor.
A mkey não precisa ser memorizada. Ela encripta as ckeys; as ckeys encriptam os secrets.
ckey (content key)
Fornecida pelo operador em runner add --ckey. Uma por app (ou compartilhada entre apps do mesmo repo, em multi-service compose). Encripta .env e .secrets da app, gerando .runner/secrets.enc que vai para o repositório.
A ckey deve ser salva em password manager. Sem ela, o bundle no repo é ilegível — os secrets precisam ser reconfigurados manualmente.
| mkey | ckey | |
|---|---|---|
| Gerada por | runner init |
Operador (ou --ckey sem valor) |
| Escopo | Por servidor | Por app |
| Encripta | ckeys no state | .env + .secrets |
| Armazenada em | /opt/runner/.keys/master.key |
State file (encriptada com mkey) |
| Se perder | Gerar nova no runner init + repassar ckeys |
Secrets perdidos — reconfigurar manualmente |
| Precisa memorizar? | Não | Sim (password manager) |
Adicionar ckey a um app já registrado (v2.43.3+)
O jeito canônico de definir a ckey é no registro: runner add --ckey [<chave>].
Mas se a app já está registrada sem ckey e você tenta salvar um secret, o
Runner barra:
$ runner env set minha-app DB_PASSWORD s3cr3t --secret
ERROR Secrets requerem encriptacao. ...A partir do v2.43.3 dá pra anexar uma ckey sem re-registrar (não precisa
unregister + add):
Opção 1 — runner deploy --ckey (caminho ordinário):
runner deploy minha-app --ckey # gera um ckey AES-256-GCM novo
runner deploy minha-app --ckey "minha-chave-forte" # usa a chave informada--ckey sozinho gera; --ckey <chave> usa a informada. Grava no state.ckey
(scope app-state), então o env set --secret / secrets set passam a funcionar.
No-op (com aviso) se a app já tem ckey — nunca sobrescreve (quebraria o bundle).
Não combina com --all.
Opção 2 — prompt interativo no env set --secret:
$ runner env set minha-app DB_PASSWORD s3cr3t --secret
App 'minha-app' não tem ckey (necessário pra secrets). Gerar e registrar um ckey agora? [y/N] y
✓ Ckey gerado e registrado para 'minha-app' (ck_xxxx…xxxx).
Secret 'DB_PASSWORD' salvo em .secrets (encriptado)y → gera, anexa e segue salvando o secret. Em modo -o json / não-interativo
(CI), mantém o erro estruturado (E7002) — sem prompt.
Não confunda com
runner ckeys add <project>: esse só cria a chave no vault (pré-registro / compose), não grava no state do app — por isso sozinho não destrava oenv set --secret. Para anexar a um app registrado, use as opções acima.
⚠️ A ckey gerada também precisa ir pro password manager (
runner ckeys reveal <app> --insecuremostra o valor) — sem ela o bundle no repo fica ilegível.
Fases do ciclo
Phase 1: definição no manifest
environment: e secrets: no .deploy.yml definem as variáveis. Valores podem ser literais, prompts interativos ou geradores automáticos:
environment:
FLASK_ENV: production # literal
BASE_DOMAIN: "{{::Domain?app.exemplo.com}}" # prompt com default
secrets:
DB_PASSWORD: "{RANDOM}" # gera 32 chars alfanuméricos
JWT_SECRET: "${GENERATE:hex:64}" # gera 64 chars hex
SMTP_PASSWORD: "{{::SMTP Password}}" # prompt (input mascarado)Phase 1.4: pull secrets (próximo deploy após bundle existir)
No início de cada deploy, o Runner lê .runner/secrets.enc do repo (baixado via git), decripta com a ckey local, e popula .env e .secrets plaintext temporários. Se env_sync: remote (default), o local é sobrescrito pelo bundle remoto quando o hash muda.
.runner/secrets.enc (repo) ──▶ decripta com ckey ──▶ .env + .secrets (local)Para manter o local como fonte de verdade (operador fez mudanças diretas), usar env_sync: skip no .deploy.yml:
env_sync: skip # local soberano — runner nunca sobrescreve .env/.secretsPhase 7: push secrets
Após provisionar ou alterar variáveis, o Runner encripta .env + .secrets com a ckey usando AES-256-GCM, grava em .runner/secrets.enc, commita e faz push no repo.
.env + .secrets (local) ──▶ encripta com ckey ──▶ .runner/secrets.enc ──▶ git pushO formato interno do bundle:
[12 bytes nonce][ciphertext AES-256-GCM]Conteúdo decriptado:
---ENV---
FLASK_ENV=production
BASE_DOMAIN=app.exemplo.com
---SECRETS---
DB_PASSWORD=a8Kp2m...
JWT_SECRET=7f3a9b...Phase 8: re-encriptação após mudança local
Quando o operador usa runner env set para alterar uma variável, o próximo deploy re-encripta e faz push do bundle atualizado.
runner env set minha-app --key BASE_DOMAIN --value novo.exemplo.com
runner deploy minha-app
# → Phase 7 executa: re-encripta .env + .secrets, push secrets.encRecuperação (servidor morre)
# 1. Novo servidor: inicializar runner (nova mkey)
runner init
# 2. Re-registrar com a MESMA ckey salva no password manager
runner add \
--repo usuario/minha-app \
--branch main \
--token ghp_xxx \
--ckey "ckey-salva-no-password-manager"
# 3. Runner detecta .runner/secrets.enc no repo, decripta com ckey
# e restaura .env + .secrets automaticamente
# 4. Deploy funciona sem intervenção
runner deploy minha-appComandos de gestão
Inspecionar secrets
runner secrets show minha-app # decripta e exibe (requer mkey + ckey)
runner secrets diff app-a app-b # compara decriptados entre duas appsAlterar variáveis
runner env set minha-app --key CHAVE --value valor
runner env set minha-app --key SENHA --value valor --secret # força para .secretsForçar re-sincronização do bundle
# v2.30.3 — re-encripta a partir do plaintext local (local é a fonte de verdade)
runner secrets reset-bundle minha-appÚtil quando o bundle remoto ficou fora de sincronia (ex: operador editou .env diretamente).
Recuperar plaintext do bundle local
# v2.30.10 — decripta .runner/secrets.enc local e escreve .env/.secrets
runner secrets pull minha-appÚtil para inspecionar o que está no bundle sem fazer um deploy completo.
Multi-service: unificar ckeys
Quando N apps compartilham o mesmo repo (docker-compose multi-service), todas devem usar a mesma ckey — o Runner armazena .runner/secrets.enc na raiz do repo.
# v2.30.6 — copia a ckey de app-a para app-b
runner ckeys copy --from app-a --to app-b
# Verificar quais ckeys existem
runner ckeys listApós unificar, rodar runner secrets reset-bundle em cada app para regenerar o bundle com a ckey comum. Para o fluxo completo multi-service, veja Wizard state machine.
Segurança
| O que é seguro | Motivo |
|---|---|
Commitar .runner/secrets.enc no repo |
AES-256-GCM com ckey que não está no repo |
| State file no servidor | ckey armazenada encriptada com mkey (modo 600) |
.secrets no servidor |
Arquivo com modo 600, não commita no git |
| O que causa perda irreversível | Ação |
|---|---|
| Perder a ckey sem backup | Secrets precisam ser reconfigurados manualmente |
Deletar .runner/secrets.enc do repo sem ter .env/.secrets locais |
Bundle perdido |
Recomendações:
- Uma ckey por app (ou por bundle compartilhado em multi-service)
- Salvar ckey em password manager imediatamente após
runner add --ckey - Não reutilizar ckeys entre projetos de clientes diferentes
- Rotacionar mkey periodicamente:
runner mkey rotate --force
O que pode dar errado
| Erro | Causa | Como resolver |
|---|---|---|
Secrets não restaurados no runner add |
.env já existe localmente |
Remover .env local e re-registrar |
| Decriptação falha | ckey incorreta | runner add --ckey "ckey-correta" |
| Bundle fora de sincronia | .env editado diretamente sem runner env set |
runner secrets reset-bundle minha-app |
| Apps multi-service sobrescrevem bundle | ckeys diferentes para apps no mesmo repo | runner ckeys copy --from a --to b, depois reset-bundle |
${VAR} literal no bundle |
Placeholder não expandido no wizard | Ver build.args lifecycle |