Ativos persistentes (`assets:`)
O campo assets: organiza os dados persistentes da app em quatro categorias
semânticas — secrets, state, cache e config. É açúcar tipado sobre
volumes: (que continua válido como fallback low-level): cada categoria
define o modo de montagem, a localização no host e as regras de ciclo de vida.
volumes:continua funcionando.assets:é opt-in — você adota apenas os assets que faz sentido tipar.
Pré-requisitos
- Runner v2.37.0 ou superior
- App registrada e com
.deploy.ymlno repo
As quatro categorias
assets:
secrets: # Operador sobe 1×; montado :ro no container
- source: poc/secrets # host: <app>/data/poc/secrets
mount: /app/poc/secrets
required: true # deploy aborta se dir ausente/vazio
docs: "Coloque oauth-token.json + oauth-client.json aqui."
state: # App escreve; persiste entre versões; :rw
- source: poc/out
mount: /app/poc/out
backup: daily # metadado para BackupAlly (runner não agenda)
cache: # Ephemeral/rebuildável; :rw
- source: cache/next
mount: /root/.cache
ttl: 30d # metadado para `assets clean` (Fase 2 — v2.38.0)
config: # Arquivo do repo na versão deployada; :ro
- source_in_repo: livekit.prod.yaml
mount: /etc/livekit.yaml| Categoria | Modo | Quem escreve | Atualiza no deploy |
|---|---|---|---|
secrets |
:ro |
Operador (upload manual) | Não |
state |
:rw |
App em runtime | Não (persiste entre versões) |
cache |
:rw |
App em runtime | Não (rebuildável) |
config |
:ro |
Repo (vem do código) | Sim (sempre a versão atual) |
Layout no host
Todos os assets (exceto config) vivem sob <app>/data/<source>:
/data/apps/<app>/
├── data/
│ ├── poc/secrets/ ← assets.secrets (source: poc/secrets)
│ ├── poc/out/ ← assets.state (source: poc/out)
│ └── cache/next/ ← assets.cache (source: cache/next)
├── src/<instance>/<ver>/
│ └── livekit.prod.yaml ← assets.config (source_in_repo: livekit.prod.yaml)
├── logs/
└── .runner/secrets.encA categoria config é montada direto da versão deployada no repositório
(src/<instance>/<versão>/) — o arquivo é atualizado a cada deploy.
Validação `required:` — aborta antes de qualquer container
Quando required: true, o runner verifica no pré-flight que o dir
<app>/data/<source> existe e não está vazio. Se estiver ausente ou vazio,
o deploy é abortado com a mensagem do campo docs::
[asset_required_missing] asset 'poc/secrets' (secrets) está vazio/ausente.
Coloque oauth-token.json + oauth-client.json aqui.
Path: /data/apps/capital28-dashboard/data/poc/secretsA verificação ocorre após o refresh do manifesto (pós-fetch/git pull) e antes de qualquer ação de container — não há rollback necessário porque nenhum container foi tocado.
required: é ignorado (com aviso de validação) em cache, pois cache é
ephemeral por definição.
Como popular um asset de secrets antes do primeiro deploy:
mkdir -p /data/apps/<app>/data/poc/secrets
cp oauth-token.json oauth-client.json /data/apps/<app>/data/poc/secrets/
runner deploy <app> # agora o required passaChown automático para imagens non-root
Ao criar um diretório de state ou cache pela primeira vez, o runner
inspeciona o campo Config.User da imagem (via docker inspect) e executa
chown -R <user> <dir> se o user for não-root. Isso garante que a app
consiga escrever nos diretórios sem precisar de configuração extra.
secretsnão recebe chown de escrita (montado:ro).- Se o chown falhar (ex: user nominal não resolvível no host), o runner emite
WARNe prossegue — o deploy não é abortado. - O chown é aplicado apenas na criação do diretório (idempotente em runs seguintes).
Mounts automáticos: `data`, `logs` e `keys`
Para deploys do tipo docker-build, o runner monta automaticamente três
diretórios do host quando eles existem, sem precisar declará-los:
| Dir no host | Mount no container | Modo |
|---|---|---|
<app>/data/ |
/app/data |
:rw |
<app>/logs/ |
/app/logs |
:rw |
<app>/keys/ |
/app/keys |
:ro |
Basta criar o diretório no host para ativar o auto-mount:
mkdir -p /data/apps/<app>/logs # a partir do próximo deploy, /app/logs fica montadoSe você declarar um asset ou volumes: com o mesmo destino (/app/data,
/app/logs ou /app/keys), o auto-mount é suprimido para esse diretório —
não há duplicidade.
Campos `backup:` e `ttl:` são apenas metadados
O runner não agenda backups nem expira cache automaticamente:
backup:— metadado lido pelo BackupAlly para definir a frequência de snapshot dos dados da app.ttl:— será usado pelo comandorunner assets cleanna Fase 2 (v2.38.0).
Tabela de ciclo de vida
O que cada comando preserva ou remove nos artefatos da app:
| Comando | src/<ver>/ |
data/ (assets) |
logs/ |
Named volumes | .runner/secrets.enc |
|---|---|---|---|---|---|
deploy |
recriado/LRU (keep_versions) |
preservado | preservado | preservado | preservado |
rollback |
symlink aponta para versão anterior | preservado | preservado | preservado | preservado |
archive |
contêineres parados; dirs preservados | preservado | preservado | preservado | preservado |
unregister |
removido | REMOVIDO | REMOVIDO | preservado (Docker) | removido |
reset (soft) |
versões e symlink removidos | preservado | preservado | preservado | preservado |
reset --hard |
src/<instance>/ inteiro removido |
preservado | preservado | preservado | .env/.secrets regenerados do manifesto |
Atenção: `unregister` remove tudo
unregister executa remove_dir_all no diretório da app — isso apaga
data/, logs/, .runner/ e tudo mais. Named volumes Docker são
gerenciados pelo próprio Docker e não são removidos.
Antes de unregister, faça snapshot dos assets:
# Fase 2 (v2.38.0) terá `runner assets snapshot <app>`.
# Por ora, backup manual:
tar -C /data/apps/<app> -czf /tmp/<app>-assets-backup.tar.gz data logs
runner unregister <app>`reset --hard` preserva os dados
Ao contrário do que o --help sugere ("wipe .env, .secrets, and
persistent_dirs"), o --hard remove apenas src/<instance>/ (a árvore de
versões do deploy). data/, logs/ e .runner/secrets.enc são preservados.
O que acontece com .env/.secrets é regeneração: o runner re-executa o
wizard com os valores declarados no manifesto.
Use unregister seguido de re-registro se precisar começar do zero
completamente.
Validação de schema
O runner valida as entradas de assets: no runner validate:
| Regra | Erro |
|---|---|
source absoluto ou com .. |
asset_source_invalid |
mount relativo (não começa com /) |
asset_mount_invalid |
mount igual a outro asset ou volumes: |
asset_mount_collision |
required: true em cache |
warning asset_required_on_cache |
required: true e dir vazio no deploy |
asset_required_missing (aborta) |
Coexistência com `volumes:`
assets: e volumes: coexistem no mesmo manifesto. Use volumes: para
casos que não se encaixam nas categorias (ex: named volumes compartilhados
entre apps, mounts absolutos de paths fixos do servidor):
assets:
secrets:
- source: credentials
mount: /app/credentials
required: true
volumes:
- shared-queue:/app/queue:rw # named volume compartilhado com outra app
- /srv/certs:/app/certs:ro # path absoluto do servidorMounts declarados em volumes: entram no conjunto de destinos explícitos —
o runner não duplica auto-mounts para eles.