Sugestões de persistência
A plataforma observa seus apps e, quando percebe que um deles precisa guardar dados mas não tem onde, cria uma sugestão: uma mensagem curta explicando o problema e oferecendo a solução. A plataforma nunca cria o recurso sozinha — ela sugere e espera você autorizar, seja uma pessoa no painel ou um agent pela API. É o mesmo princípio em todo lugar: a Nebular detecta, propõe e só age com sua permissão.
Quando uma sugestão aparece
Seção intitulada “Quando uma sugestão aparece”Dois gatilhos geram sugestões:
- Detecção no build. Ao preparar o deploy, a plataforma olha as dependências do app (o driver de banco ou o ORM que ele usa) e deduz o tipo de persistência que ele vai querer. Por exemplo: um app Node com
better-sqlite3, ou um app Go commodernc.org/sqlite, embarca um banco SQLite e vai precisar de disco persistente. - Falha no deploy (sinal mais preciso). Quando o app sobe e quebra por falta de persistência, o erro diz exatamente o que faltou:
activate_readonly_write: o app tentou gravar num caminho somente-leitura (por exemplo/app/data). A sugestão já vem com o caminho exato.- Conexão recusada a um banco (
activate_app_crashedcujo log mostra “connection refused” numa porta de banco) e o serviço não tem banco ligado. unresolved_reference: o deploy aponta para um serviço de banco que não existe.
A sugestão não atrapalha o deploy: ela é informativa e fica guardada até você aceitar ou descartar. Se o recurso continua faltando, o deploy segue falhando com a mesma mensagem de sempre — nada é escondido nem quebrado.
As duas soluções: volume ou banco
Seção intitulada “As duas soluções: volume ou banco”A diferença mais importante é o que o app precisa:
- SQLite embarcado → volume. O app já tem o banco dentro dele (um arquivo SQLite); só falta um disco que sobreviva aos deploys. A sugestão oferece criar um volume no caminho onde o app grava (por exemplo
/app/data). Nada muda no código do app. - Driver externo (PostgreSQL, MySQL, MongoDB) → banco gerenciado. O app fala com um banco que roda fora dele e que ainda não existe. A sugestão oferece criar um banco gerenciado e ligar automaticamente com a referência
${{<db>.DATABASE_URL}}(onde<db>é o nome do serviço-banco criado). Veja como essa ligação funciona em Conexão e referências.
Em ambos os casos a Nebular corrige a si mesma para aceitar o app como ele é: ela nunca edita o código do seu app.
A plataforma nunca cria sozinha
Seção intitulada “A plataforma nunca cria sozinha”Toda sugestão nasce no estado “sugerida” e fica parada até uma decisão:
- Aceitar cria o recurso (volume ou banco) e o liga ao serviço. O próximo deploy já usa.
- Descartar arquiva a sugestão. Ela não volta a nascer pelo mesmo sinal na mesma janela (sem spam).
Quem decide é você: uma pessoa no painel ou um agent pela API. Enquanto ninguém autoriza, nada é criado e nenhum dado é tocado.
Como um agent lê as sugestões
Seção intitulada “Como um agent lê as sugestões”Liste as sugestões abertas de um projeto com o escopo projects:read (Tokens e escopos):
curl -sS "$NEBULAR_API/v1/projects/$P/suggestions" \ -H "Authorization: Bearer $NEBULAR_TOKEN"Cada item traz o diagnóstico já pronto para mostrar a um humano, no campo detail:
{ "items": [ { "id": "01j9zksf51s25jd7735q8fefyf", "service_id": "01j9zktb8c3m0xr4v6p2n1qd7e", "service_name": "polylang", "project_id": "01j9zkt0a2b3c4d5e6f7g8h9jk", "kind": "volume", "engine": "", "target_path": "/app/data", "source": "runtime_readonly", "status": "suggested", "detail": "Este app guarda dados em /app/data, mas o disco do app é somente-leitura. Posso criar um volume persistente em /app/data para os dados sobreviverem aos deploys. Criar agora?", "created_at": "2026-10-12T12:00:00Z" } ]}Os campos de cada sugestão:
| Campo | O que é |
|---|---|
id |
identificador da sugestão (use-o para descartar ou aceitar) |
service_id, service_name |
o serviço que precisa de persistência |
project_id |
o projeto do serviço |
kind |
volume (disco persistente) ou database (banco gerenciado) |
engine |
motor do banco (sqlite, postgres, mysql, mongo) ou vazio quando é volume |
target_path |
caminho do volume (ex.: /app/data); nulo quando é banco |
source |
o gatilho: build_detect, runtime_readonly, runtime_connrefused ou unresolved_ref |
status |
suggested, dismissed, accepted ou fulfilled |
detail |
mensagem em linguagem simples, pronta para a tela |
created_at |
quando a sugestão foi criada |
Como um agent descarta uma sugestão
Seção intitulada “Como um agent descarta uma sugestão”Para arquivar uma sugestão que não faz sentido, use o escopo projects:write:
curl -sS -X POST "$NEBULAR_API/v1/projects/$P/suggestions/$SID/dismiss" \ -H "Authorization: Bearer $NEBULAR_TOKEN"A sugestão passa a dismissed e não reaparece pelo mesmo sinal na mesma janela.
Aceitar (criar o recurso) ainda vem a seguir
Seção intitulada “Aceitar (criar o recurso) ainda vem a seguir”Aceitar uma sugestão — ou seja, de fato criar o volume ou o banco e ligá-lo ao serviço — está sendo finalizado e ainda não está disponível nesta versão da API. A rota prevista é:
POST /v1/projects/{pid}/suggestions/{sid}/accept (escopo de escrita)O formato exato do corpo dessa chamada ainda não está definido e será documentado aqui quando a criação por aceite entrar no ar. Até lá, você pode criar o recurso sugerido pela rota direta: um volume para o caso SQLite embarcado, ou um banco gerenciado com a referência de conexão para o caso de driver externo.
Paridade painel e API
Seção intitulada “Paridade painel e API”Tudo o que o painel mostra e faz com sugestões, um agent faz pela API /v1 — listar e descartar hoje, aceitar quando a criação por aceite entrar. Veja também o Contrato de App Nebular, que lista os erros de deploy que disparam estas sugestões, e o cookbook Como corrigir cada erro de deploy com o remédio de cada código. Quando a sugestão é um banco e o app usa SQLite embarcado, uma alternativa ao volume é migrar para libSQL gerenciado.