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 init gera a master key)
  • App registrada com runner add --ckey — ou, se já registrada sem ckey, anexe uma com runner 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 vars

As 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 o env 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> --insecure mostra 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/.secrets

Phase 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 push

O 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.enc

Recuperaçã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-app

Comandos 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 apps

Alterar variáveis

runner env set minha-app --key CHAVE --value valor
runner env set minha-app --key SENHA --value valor --secret   # força para .secrets

Forç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 list

Apó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
By Borlot.com.br on 27/05/2026