Pular para o conteúdo

Backup e restore

Escopo: databases:write. Substitua $P (projeto) e $DB (serviço-banco) pelos ids.

  • Backup diário automático: retenção de 7 diários + 4 semanais.
  • Backup sob demanda quando quiser (antes de uma migration arriscada, por exemplo).
  • Apagar o banco faz um backup final por padrão.
Janela do terminal
SUF=$(date +%s)-$RANDOM
H="Authorization: Bearer $NEBULAR_TOKEN"
P=$(curl -sS -X POST "$NEBULAR_API/v1/projects" -H "$H" -H "Content-Type: application/json" \
-d "{\"name\": \"bk-$SUF\"}" | jq -r .id)
DB=$(curl -sS -X POST "$NEBULAR_API/v1/projects/$P/databases" -H "$H" -H "Content-Type: application/json" \
-d '{"name": "db", "engine": "postgres", "quota_mb": 1024}' | jq -r .id)
# o banco nasce em provisioning: espere ficar ready antes de pedir backup
for i in $(seq 1 40); do
[ "$(curl -sS "$NEBULAR_API/v1/projects/$P/databases/$DB" -H "$H" | jq -r .status)" = ready ] && break
sleep 2
done
curl -sS -X POST "$NEBULAR_API/v1/projects/$P/databases/$DB/backups" \
-H "$H" \
-H "Idempotency-Key: backup-$DB-$(date +%Y%m%d%H%M)-$RANDOM"
{"backup_id": "b_01J9ZK5Q", "job_id": "j_01J9ZK5R"}

A resposta é 202: o backup é assíncrono. Guarde backup_id.

Paginação por cursor (next_cursor / has_more). Cada item tem id, at, size, status.

Janela do terminal
SUF=$(date +%s)-$RANDOM
H="Authorization: Bearer $NEBULAR_TOKEN"
P=$(curl -sS -X POST "$NEBULAR_API/v1/projects" -H "$H" -H "Content-Type: application/json" \
-d "{\"name\": \"bk-$SUF\"}" | jq -r .id)
DB=$(curl -sS -X POST "$NEBULAR_API/v1/projects/$P/databases" -H "$H" -H "Content-Type: application/json" \
-d '{"name": "db", "engine": "postgres", "quota_mb": 1024}' | jq -r .id)
# o banco nasce em provisioning: espere ficar ready antes de pedir backup
for i in $(seq 1 40); do
[ "$(curl -sS "$NEBULAR_API/v1/projects/$P/databases/$DB" -H "$H" | jq -r .status)" = ready ] && break
sleep 2
done
curl -sS -X POST "$NEBULAR_API/v1/projects/$P/databases/$DB/backups" -H "$H" >/dev/null
curl -sS "$NEBULAR_API/v1/projects/$P/databases/$DB/backups" \
-H "$H"
{"items": [{"id": "b_01J9ZK5Q", "at": "2026-10-10T03:00:00Z", "size": 1048576, "status": "running"}], "next_cursor": null, "has_more": false}

O item nasce running e vira done. Espere o backup concluir antes de usá-lo (restaurar um backup que ainda roda responde 409 backup_not_ready): consulte o job com GET /v1/projects/{p}/databases/{s}/jobs/{job_id} (status: queued, running, done, failed; no failed vem error com code e message), com backoff de 2 s dobrando até 30 s, teto de uns 10 min.

target:

  • "new" (padrão): restaura para um banco novo; informe new_db_name. Seguro, não toca nos dados atuais.
  • "same": sobrescreve o banco atual. Destrutivo para os dados atuais: no painel exige step-up (confirmação recente de identidade); pela API de token vale o escopo databases:write, então só use com autorização explícita de uma pessoa.
Janela do terminal
SUF=$(date +%s)-$RANDOM
H="Authorization: Bearer $NEBULAR_TOKEN"
P=$(curl -sS -X POST "$NEBULAR_API/v1/projects" -H "$H" -H "Content-Type: application/json" \
-d "{\"name\": \"bk-$SUF\"}" | jq -r .id)
DB=$(curl -sS -X POST "$NEBULAR_API/v1/projects/$P/databases" -H "$H" -H "Content-Type: application/json" \
-d '{"name": "db", "engine": "postgres", "quota_mb": 1024}' | jq -r .id)
# o banco nasce em provisioning: espere ficar ready antes de pedir backup
for i in $(seq 1 40); do
[ "$(curl -sS "$NEBULAR_API/v1/projects/$P/databases/$DB" -H "$H" | jq -r .status)" = ready ] && break
sleep 2
done
R=$(curl -sS -X POST "$NEBULAR_API/v1/projects/$P/databases/$DB/backups" -H "$H")
B=$(echo "$R" | jq -r .backup_id); J=$(echo "$R" | jq -r .job_id)
# espera o job do backup concluir (backoff 2 s, dobrando até 30 s)
delay=2
for i in $(seq 1 40); do
st=$(curl -sS "$NEBULAR_API/v1/projects/$P/databases/$DB/jobs/$J" -H "$H" | jq -r .status)
echo "tentativa $i: $st"
[ "$st" = done ] && break
case "$st" in failed) exit 1 ;; esac
sleep "$delay"; delay=$(( delay * 2 > 30 ? 30 : delay * 2 ))
done
[ "$st" = done ]
curl -sS -X POST "$NEBULAR_API/v1/projects/$P/databases/$DB/restore" \
-H "$H" -H "Content-Type: application/json" \
-H "Idempotency-Key: restore-$DB-$B" \
-d "{\"backup_id\": \"$B\", \"target\": \"new\", \"new_db_name\": \"n_restaurado\"}"
{"job_id": "j_01J9ZK5S"}

Resposta 202. O restore é assíncrono: consulte o resultado com backoff até concluir.

Motor Efeito do restore
PostgreSQL / MariaDB, new cria um banco extra na mesma instância; a plataforma monta a DATABASE_URL do banco novo (mesmo papel e senha, outro nome de banco). Aponte o app para ele quando validar.
PostgreSQL / MariaDB, same sobrescreve o banco atual; as variáveis não mudam.
SQLite, new ou same a URL e o token MUDAM em todo restore (o namespace antigo é removido no same). A plataforma sela o token novo, atualiza LIBSQL_URL/LIBSQL_AUTH_TOKEN e marca os dependentes para redeploy: faça deploy deles.
Resposta O que fazer
401 / 403 Falta databases:write. Pare e peça a uma pessoa.
409 backup_not_ready O backup ainda não terminou (job running): espere o job e repita.
404 not_found Banco ou backup_id inexistente ou de outro time. Liste os backups de novo.
409 Há outra operação em andamento no banco, ou new_db_name já existe. Espere ou troque o nome.
422 quota_exceeded O restore não cabe na cota. Aumente a cota ou apague dados.
503 unavailable Repita com backoff e a mesma Idempotency-Key; persistindo, pare e avise.
429 rate_limited Espere Retry-After.

Veja Erros e Rate limit e retry.