Pular para o conteúdo

Como corrigir cada erro de deploy (cookbook)

Esta página é o destino do fix_url de cada erro de deploy. Cada error_code tem uma seção com âncora estável: o fix_url de um erro activate_readonly_write aponta para #activate_readonly_write aqui. Chegue direto pela âncora, leia o que significa, aplique como corrigir e confira de quem é a culpa (código do app ou configuração da plataforma).

Antes de mexer no código, leia o log de build e o log do container (Logs) — o error_code diz a categoria, o log diz a causa exata. Para a tabela completa com HTTP e ação (repetir/corrigir/parar), veja Tabela de erros; para o passo a passo de diagnóstico, Fluxo de diagnóstico; para as regras normativas do app, Contrato de App Nebular.

Erros de ativação (o app subiu e não ficou de pé)

Seção intitulada “Erros de ativação (o app subiu e não ficou de pé)”

O build terminou e a plataforma tentou ativar o container; ele não respondeu como esperado. Quase sempre é código ou configuração do app.

O que significa: o app está rodando e estável, mas não respondeu HTTP na porta esperada dentro da janela de ativação.

Como corrigir:

  1. Escute em 0.0.0.0:$PORT (a Nebular injeta PORT), nunca em 127.0.0.1, localhost ou porta fixa. Portas abaixo de 1024 não existem no runtime.
  2. Garanta que a port declarada no serviço é a mesma em que o app escuta.
  3. Abra a porta antes de qualquer trabalho pesado (migration, aquecimento de cache, chamada a terceiros).
  4. Confira o healthcheck_path: ele precisa responder 2xx/3xx sem efeitos colaterais. Se devolve 4xx/5xx, o app conta como sem HTTP.

Culpa: código do app. Detalhe em Contrato de App e App pronto para escalar.

O que significa: o app tentou gravar num caminho somente-leitura. A raiz e /app não aceitam escrita; só volume montado ou /tmp/$TMPDIR (64 MB, em memória, apagado a cada deploy).

Como corrigir:

  1. Descubra o caminho que o app quis gravar (a mensagem do erro traz o caminho; o log do container confirma).
  2. Se é dado temporário, aponte-o para /tmp ou $TMPDIR.
  3. Se é dado que precisa sobreviver ao deploy (banco SQLite, uploads, índice), monte um volume nesse caminho e leia o caminho de uma variável de ambiente (DB_FILE, DATA_DIR), nunca fixo no código.
  4. Se o arquivo pode ser gerado no build (assets, bundle), gere-o no build, não no boot.

Banco SQLite gravando em disco somente-leitura? Em vez de um volume, considere mover o banco para um banco gerenciado libSQL — recomendado para apps sem estado que escalam. A plataforma também pode ter registrado uma sugestão de persistência para este caso.

Culpa: código do app (gravação em caminho errado), resolvida com um recurso da plataforma (volume) ou mudança para banco gerenciado.

O que significa: o app encerrou logo depois de iniciar. A mensagem do erro traz a primeira linha de erro do log.

Como corrigir:

  1. Leia o log do container (Logs) — a causa quase sempre está na primeira linha de erro.
  2. As causas mais comuns, em ordem:
    • Variável de ambiente faltando. Defina as envs do serviço; o app deve falhar no boot com mensagem clara quando falta algo crítico, não cair em silêncio.
    • Arquivo ausente (config, template, binário que não saiu no lugar certo).
    • Escrita em disco somente-leitura no boot → trate como activate_readonly_write.
    • Conexão recusada a um banco (connection refused numa porta de banco) → o serviço não tem banco ligado; ligue por referência (DATABASE_URL = ${{db.DATABASE_URL}}, veja Conexão e referências) ou, para SQLite, migre para libSQL gerenciado.
  3. Corrija a causa e dispare novo deploy. A versão anterior continua no ar enquanto isso.

Culpa: código ou configuração do app. Quando o log mostra tentativa de criar/gravar o arquivo de banco em disco somente-leitura, trate como falta de persistência (volume ou banco gerenciado).

O que significa: o app usou mais memória que o limite do serviço e foi encerrado (OOM) no runtime.

Como corrigir:

  1. Reduza o consumo: diminua caches em memória, concorrência, tamanho de pool e workers.
  2. Procure vazamentos (memória que só cresce) no log e nas métricas.
  3. Se o consumo é legítimo, suba resources.memory_mb do serviço ou troque de plano (Escala e recursos).

Culpa: código do app (consumo) ou configuração (limite baixo demais para a carga real).

O que significa: a imagem declara ou assume USER root. A plataforma a força a rodar como 65534 (não-root) e o container encerrou nos primeiros 30 s.

Como corrigir:

  1. Declare build.user no serviço (uid[:gid], nunca root) com um usuário que a imagem suporte.
  2. Ou troque por uma variante da imagem sem privilégio (muitas imagens oficiais têm tag -nonroot/-slim com usuário não-root).
  3. Garanta que os diretórios que o app escreve pertencem a esse usuário (ou são volume//tmp).

Culpa: código do app / imagem. A plataforma não roda container como root por política de isolamento (Isolamento).

A falha aconteceu antes de ativar: ao clonar, detectar a stack, instalar, compilar ou copiar o artefato. Leia sempre o log de build primeiro.

O que significa: a plataforma não conseguiu clonar o repositório.

Como corrigir:

  1. Confira o repositório e a branch configurados no serviço.
  2. Confira a instalação do GitHub App no serviço (acesso ao repositório pode ter sido revogado).
  3. Em repositório privado, confirme que a instalação cobre aquele repositório.

Culpa: configuração (acesso/repo/branch), raramente do app.

O que significa: a plataforma não conseguiu detectar automaticamente a stack do app (qual builder usar).

Como corrigir:

  1. Declare build.builder explicitamente no serviço (ex.: nixpacks, dockerfile) em vez de depender da detecção.
  2. Em monorepo, defina o root_dir correto para a plataforma olhar a pasta certa (Monorepo).
  3. Confirme que os arquivos-sinal da stack existem na raiz do build (package.json, go.mod, requirements.txt, Dockerfile…).

Culpa: configuração (builder/root_dir não declarados).

O que significa: a instalação de dependências falhou (o gerenciador de pacotes retornou erro).

Como corrigir:

  1. Leia o log de build: ele traz a dependência e o motivo (versão inexistente, rede, conflito de resolução).
  2. Fixe versões e comite o lockfile (package-lock.json, go.sum, Cargo.lock, poetry.lock) — build não deve resolver “latest”.
  3. Remova dependências que exigem rede fora do registro padrão ou toolchain que não está na imagem de build.

Culpa: código do app (dependências/lockfile). Se a falha é claramente de rede transitória, repita o deploy.

O que significa: o comando de build (compilar/empacotar) retornou código diferente de 0.

Como corrigir:

  1. Reproduza o build localmente com o mesmo comando; corrija o erro de compilação/empacotamento.
  2. Se o comando detectado não é o que você quer, declare build_command explicitamente no serviço.
  3. Garanta que o artefato sai no caminho esperado (em Go, o binário deve sair em /app/app; veja Go).

Culpa: código do app (erro de build) ou configuração (build_command errado).

O que significa: a plataforma não conseguiu copiar o artefato produzido para a imagem final — em geral o artefato não está onde ela espera.

Como corrigir:

  1. Confirme o caminho de saída do artefato (binário/bundle) e alinhe-o ao esperado (ex.: /app/app em Go).
  2. Em monorepo, confira root_dir e os caminhos relativos do build_command.
  3. Verifique que o build realmente gerou o arquivo (um build_command que “passa” sem produzir saída cai aqui).

Culpa: código do app / configuração (caminho de saída).

O que significa: falha genérica de build quando a plataforma não classificou uma fase específica. Trate como “leia o log e isole a fase”.

Como corrigir:

  1. Leia o log de build de cima para baixo: a primeira linha de erro aponta a fase real (clone, detect, install, script, copy).
  2. Vá para a seção específica desta página conforme a fase (install_failed, script_failed, detect_failed, build_copy_failed, build_clone_failed).
  3. Se nenhuma fase se encaixa e o app está correto, não é culpa do app — reporte o error_code, o id do deploy e a hora.

Culpa: depende da fase que o log revela.

O que significa: o build estourou a memória disponível e foi encerrado (distinto de activate_oom, que é no runtime).

Como corrigir:

  1. Reduza o paralelismo do build (ex.: -j1/--max-old-space-size menor, menos workers).
  2. Tire etapas pesadas do build (geração de assets gigantes, testes no build).
  3. Reduza o que entra no artefato (.dockerignore, menos dependências de build).

Culpa: código do app (build pesado).

O que significa: o build passou do tempo máximo permitido.

Como corrigir:

  1. Enxugue o build: cache de dependências via lockfile, menos etapas, nada de download gigante a cada build.
  2. Tire testes e tarefas longas do caminho de build.
  3. Em multi-stage, remova estágios que não contribuem para a imagem final.

Culpa: código do app (build longo).

O que significa: o espaço de build encheu durante a produção do artefato.

Como corrigir:

  1. Reduza o tamanho do artefato e dos intermediários (multi-stage descartando camadas de build, .dockerignore, limpeza de caches no mesmo passo que os cria).
  2. Evite copiar node_modules/alvos de build para a imagem final.
  3. Se o artefato já é enxuto e o erro persiste, não é culpa do app: é falta de espaço na plataforma (aparentada de build_storage_unavailable). Reporte o error_code, o id do deploy e a hora.

Culpa: código do app (artefato grande) na maioria dos casos; plataforma quando o artefato é enxuto e o erro persiste.

Erros de referência entre variáveis (${{svc.VAR}}, ${{shared.VAR}})

Seção intitulada “Erros de referência entre variáveis (${{svc.VAR}}, ${{shared.VAR}})”

As referências são resolvidas no deploy: o POST do deploy responde 422 com details.references (apenas nomes, nunca valores) e nada é criado. Corrija e refaça o deploy. Detalhe em Variáveis e referências.

O que significa: a referência aponta para uma variável ou serviço que não existe. A lista details.references diz quais.

Como corrigir:

  1. Para cada nome na lista, crie a variável ou o serviço citado, ou corrija o nome na referência.
  2. Se a referência aponta para um banco que não existe, crie o banco gerenciado e ligue por ${{<db>.DATABASE_URL}} (a plataforma pode ter registrado uma sugestão).
  3. Refaça o deploy.

Culpa: configuração do app (referência para algo que falta).

O que significa: há um ciclo — A referencia B que referencia A (direta ou indiretamente).

Como corrigir:

  1. Siga a cadeia em details.references e identifique o laço.
  2. Quebre o ciclo usando um valor literal em um dos lados em vez da referência.
  3. Refaça o deploy.

Culpa: configuração do app.

O que significa: um ${{...}} está fora da forma escopo.VAR (sintaxe inválida).

Como corrigir:

  1. Use exatamente ${{<serviço>.VAR}} ou ${{shared.VAR}} — sem espaços estranhos, sem escopo vazio, sem . a mais.
  2. Confira a grafia do nome do serviço e da variável.
  3. Refaça o deploy.

Culpa: configuração do app (sintaxe).

O que significa: a cadeia de referências tem mais de 16 níveis (uma variável que referencia outra, que referencia outra…).

Como corrigir:

  1. Achate a cadeia: faça as variáveis apontarem direto para o valor final em vez de encadear.
  2. Substitua os níveis intermediários por valores literais onde fizer sentido.
  3. Refaça o deploy.

Culpa: configuração do app (encadeamento excessivo).

O que significa: o valor expandido passou de 64 KiB, ou o ambiente total do serviço passou de 256 KiB.

Como corrigir:

  1. Reduza o valor: não guarde arquivos, certificados grandes ou blobs em variável de ambiente.
  2. Mova conteúdo grande para um volume ou um arquivo gerado no build e aponte a variável para o caminho.
  3. Refaça o deploy.

Culpa: configuração do app (valor grande demais).

Não têm âncora de remédio porque a ação é “esperar” ou “reportar”, não corrigir o app:

  • team_app_limit: o time já roda o máximo de apps do plano (a mensagem traz “N de M”). Pare ou remova um app, ou peça ao operador para ampliar o limite; depois repita.
  • build_storage_error, build_storage_unavailable, build_volume_unavailable: falha interna de armazenamento de build da plataforma (não do seu repo). Repita em alguns minutos; se persistir, reporte.
  • deploy_failed: genérico sem fase classificada — leia o log e caia na seção específica.
  • blocked, canceled, superseded: estados de deploy, não falhas do app — veja Deploys e status.

Migrar SQLite local para banco gerenciado (libSQL)

Seção intitulada “Migrar SQLite local para banco gerenciado (libSQL)”

Quando o erro é activate_readonly_write ou activate_app_crashed com o log mostrando tentativa de criar/gravar um arquivo de banco (.db, .sqlite) em disco somente-leitura, você tem duas saídas: montar um volume (mantém o SQLite em arquivo) ou migrar para libSQL gerenciado (recomendado para apps sem estado que escalam em mais de uma réplica). Esta seção é o caminho de libSQL, já aplicado em 5 apps.

Se LIBSQL_URL — ou DATABASE_URL começando com libsql://, https:// ou wss:// — estiver definido, use o banco remoto com LIBSQL_AUTH_TOKEN. Sem isso, caia no arquivo local (fallback). Logue no boot qual modo está ativo, sem imprimir o token nem a URL.

  1. No deploy que falhou, aceite a sugestão de banco (ou ligue o modo “presumir tudo” para a plataforma criar e ligar o recurso sugerido sem perguntar a cada passo). Veja Sugestões de persistência.
  2. Confirme que as variáveis foram injetadas no serviço: LIBSQL_URL, DATABASE_URL e LIBSQL_AUTH_TOKEN. São sensíveis — tratadas como segredo, nunca em NEXT_PUBLIC_*/VITE_* nem em log.
  3. Defina as demais envs do app (ex.: ENV, TOTP_KEY, PUBLIC_BASE_URL) com defaults seguros; falhe no boot se faltar algo crítico.
  4. Escute em 0.0.0.0:$PORT. A Nebular injeta PORT; por exemplo LISTEN_ADDR=0.0.0.0:8080. Nunca 127.0.0.1/localhost.
  5. Redeploy. O novo deploy sobe já falando com o banco remoto; o log do boot deve dizer “modo: libSQL remoto”.

Exemplo de cliente — Go (libsql-client-go, com fallback modernc)

Seção intitulada “Exemplo de cliente — Go (libsql-client-go, com fallback modernc)”

É código do seu app (não é chamada à API /v1 da Nebular):

// abrirBanco escolhe o modo pelo ambiente: se há LIBSQL_URL (ou DATABASE_URL
// remoto), abre o banco gerenciado; senão cai no arquivo local. Chamada uma vez
// no boot, antes de abrir a porta HTTP.
func abrirBanco() (*sql.DB, error) {
url := primeiroNaoVazio(os.Getenv("LIBSQL_URL"), os.Getenv("DATABASE_URL"))
if ehRemoto(url) { // libsql:// | https:// | wss://
token := os.Getenv("LIBSQL_AUTH_TOKEN")
dsn := url + "?authToken=" + token
log.Println("banco modo=libSQL-remoto") // nunca logar url/token
return sql.Open("libsql", dsn) // github.com/tursodatabase/libsql-client-go
}
caminho := primeiroNaoVazio(os.Getenv("DB_FILE"), "/tmp/app.db")
log.Printf("banco modo=arquivo-local path=%s", caminho)
return sql.Open("sqlite", caminho) // modernc.org/sqlite, sem CGO
}

Exemplo de cliente — Node (@libsql/client no lugar de better-sqlite3)

Seção intitulada “Exemplo de cliente — Node (@libsql/client no lugar de better-sqlite3)”
// criarCliente escolhe o modo pelo ambiente e devolve um cliente @libsql/client.
// Chamada no boot; o mesmo cliente serve arquivo local e banco remoto.
import { createClient } from "@libsql/client";
export function criarCliente() {
const url = process.env.LIBSQL_URL || process.env.DATABASE_URL || "";
const remoto = /^(libsql|https|wss):\/\//.test(url);
if (remoto) {
console.log("banco modo=libSQL-remoto"); // nunca logar url/token
return createClient({ url, authToken: process.env.LIBSQL_AUTH_TOKEN });
}
const file = process.env.DB_FILE || "/tmp/app.db";
console.log(`banco modo=arquivo-local path=${file}`);
return createClient({ url: `file:${file}` });
}
  • Dados já gravados no arquivo local NÃO migram sozinhos para o remoto. Exporte e importe o conteúdo explicitamente (dump do SQLite → carga no libSQL) antes de depender só do remoto.
  • Valide o que não vem de graça no remoto: migrations aplicadas, triggers, índices e FTS5 recriados, e PRAGMA foreign_keys = ON por conexão (não é herdado do arquivo local).
  • Mantenha o fallback local funcionando para desenvolvimento e teste: sem LIBSQL_URL, o app continua subindo contra um arquivo em /tmp ou num volume.