Primeiro deploy
Este é o fluxo base de todos os guias por stack. Escopos do token: projects:write, deploy, projects:read (veja Token de API). Todo POST que cria leva Idempotency-Key; repetir a mesma chave com o mesmo corpo devolve a mesma resposta.
Cada bloco abaixo é autossuficiente (variáveis P = id do projeto, S = id do serviço, D = id do deploy).
1. Criar o projeto
Seção intitulada “1. Criar o projeto”Um projeto é um grupo de serviços que compartilham rede e podem compartilhar variáveis por referência (Conceitos).
curl -sS -X POST "$NEBULAR_API/v1/projects" \ -H "Authorization: Bearer $NEBULAR_TOKEN" \ -H "Content-Type: application/json" \ -H "Idempotency-Key: pd-proj-$(date +%s)-$RANDOM" \ -d "{\"name\": \"meu-app-$(date +%s)-$RANDOM\"}"{"id": "01j9zkk07fhzv5zsmn996cdwpb", "name": "meu-app-1760000000"}Guarde id (P). Em erro:
| Resposta | O que fazer |
|---|---|
401 |
Token ausente ou inválido. Pare e confira o cabeçalho. |
403 forbidden |
Falta o escopo projects:write. Peça um token novo. |
409 (ex.: slug_taken, project_quota_exceeded) |
Troque o nome ou apague um projeto que não usa mais. |
2. Criar o serviço
Seção intitulada “2. Criar o serviço”Duas fontes (source) cobrem o primeiro deploy: imagem pública ou upload de código. Para visibility: "public" informe o slug (sai em https://<slug>.<domínio de apps>); omitido, a plataforma deriva um do nome. O web que todo projeto traz nasce público, já com slug e public_url (para privado: PATCH com {"visibility": "private"}; veja Conceitos).
Opção A: imagem pública
Seção intitulada “Opção A: imagem pública”SUF=$(date +%s)-$RANDOMP=$(curl -sS -X POST "$NEBULAR_API/v1/projects" \ -H "Authorization: Bearer $NEBULAR_TOKEN" -H "Content-Type: application/json" \ -H "Idempotency-Key: pd-proj2-$SUF" -d "{\"name\": \"meu-app-$SUF\"}" | jq -r .id)curl -sS -X PATCH "$NEBULAR_API/v1/projects/$P/services/web" \ -H "Authorization: Bearer $NEBULAR_TOKEN" \ -H "Content-Type: application/json" \ -d "{ \"visibility\": \"public\", \"slug\": \"meu-app-$SUF\", \"source\": {\"type\": \"image\", \"ref\": \"ghcr.io/org/app:1.2\"}, \"port\": 8080, \"healthcheck_path\": \"/healthz\" }"{"id": "01j9zk3jremq8fkd3sjt7h300r", "name": "web", "kind": "app", "visibility": "public", "port": 8080}Opção B: upload de código
Seção intitulada “Opção B: upload de código”Use "source": {"type": "upload"} e um build (builder = auto, node, go, rust, python, static ou dockerfile). Sem build, vale auto.
SUF=$(date +%s)-$RANDOMP=$(curl -sS -X POST "$NEBULAR_API/v1/projects" \ -H "Authorization: Bearer $NEBULAR_TOKEN" -H "Content-Type: application/json" \ -H "Idempotency-Key: pd-proj3-$SUF" -d "{\"name\": \"meu-site-$SUF\"}" | jq -r .id)curl -sS -X PATCH "$NEBULAR_API/v1/projects/$P/services/web" \ -H "Authorization: Bearer $NEBULAR_TOKEN" \ -H "Content-Type: application/json" \ -d "{ \"visibility\": \"public\", \"slug\": \"meu-site-$SUF\", \"source\": {\"type\": \"upload\"}, \"build\": {\"builder\": \"auto\"} }"{"id": "01j9zkngmvzxkw57zdyzz80b5e", "name": "web", "kind": "app", "visibility": "public", "port": 3000}Regras do name: ^[a-z][a-z0-9-]{0,30}$, único no projeto (é o hostname na rede privada). port padrão: 3000.
| Resposta | O que fazer |
|---|---|
400 invalid_request |
name, source.type, build.builder ou healthcheck_path inválidos: corrija o campo citado em message. |
404 not_found |
O projeto não existe ou não é do seu time (as duas situações são indistinguíveis). |
409 name_taken / slug_taken |
name ou slug já usado (todo projeto já nasce com um serviço web: use PATCH nele ou escolha outro nome). Troque. |
3. Disparar o deploy
Seção intitulada “3. Disparar o deploy”Resposta imediata: o deploy fica queued e o build roda em seguida. Guarde o id (D).
# preparo (passos 1 e 2): projeto e serviço de imagemSUF=$(date +%s)-$RANDOMH="Authorization: Bearer $NEBULAR_TOKEN"P=$(curl -sS -X POST "$NEBULAR_API/v1/projects" -H "$H" -H "Content-Type: application/json" \ -d "{\"name\": \"pd4-$SUF\"}" | jq -r .id)S=$(curl -sS -X PATCH "$NEBULAR_API/v1/projects/$P/services/web" -H "$H" -H "Content-Type: application/json" \ -d '{"source": {"type": "image", "ref": "ghcr.io/org/app:1.0"}}' | jq -r .id)
curl -sS -X POST "$NEBULAR_API/v1/projects/$P/services/$S/deployments" \ -H "$H" \ -H "Content-Type: application/json" \ -H "Idempotency-Key: pd-dep-$SUF" \ -d '{}'{"id": "01j9zkv1t9ndd76xmr8ahxee7p", "status": "queued"}Com source.type = "upload", envie o código como multipart/form-data nesta mesma rota (no lugar do corpo {}): -F "file=@fonte.tar.gz". Com git, o deploy usa o repositório e o branch do source.
| Resposta | O que fazer |
|---|---|
403 forbidden |
Falta o escopo deploy. |
409 deploy_in_progress |
Já existe um deploy em andamento para o serviço: consulte-o em vez de disparar outro. |
unresolved_reference / reference_cycle |
Variável referenciada não existe ou há ciclo: veja Variáveis e referências. |
429 |
Espere Retry-After (Rate limit e retry). |
4. Esperar o deploy (polling com backoff)
Seção intitulada “4. Esperar o deploy (polling com backoff)”Não há webhooks neste ciclo. Consulte GET .../deployments/{d} começando em 2 s e dobrando até 30 s, por no máximo cerca de 10 minutos. Terminal de sucesso: ready. Terminais de falha: failed, blocked, canceled, superseded.
# preparo (passos 1 a 3): projeto, serviço de imagem e deploySUF=$(date +%s)-$RANDOMH="Authorization: Bearer $NEBULAR_TOKEN"P=$(curl -sS -X POST "$NEBULAR_API/v1/projects" -H "$H" -H "Content-Type: application/json" \ -d "{\"name\": \"pd5-$SUF\"}" | jq -r .id)S=$(curl -sS -X PATCH "$NEBULAR_API/v1/projects/$P/services/web" -H "$H" -H "Content-Type: application/json" \ -d '{"source": {"type": "image", "ref": "ghcr.io/org/app:1.0"}}' | jq -r .id)D=$(curl -sS -X POST "$NEBULAR_API/v1/projects/$P/services/$S/deployments" -H "$H" \ -H "Content-Type: application/json" -d '{}' | jq -r .id)
delay=2for i in $(seq 1 40); do st=$(curl -sS "$NEBULAR_API/v1/projects/$P/services/$S/deployments/$D" -H "$H" | jq -r .status) echo "tentativa $i: $st" case "$st" in ready) break ;; failed|blocked|canceled|superseded) echo "deploy terminou em $st" >&2; exit 1 ;; esac sleep "$delay"; delay=$(( delay * 2 > 30 ? 30 : delay * 2 ))done[ "$st" = ready ]Em failed, leia o log de build. Em blocked, um serviço do qual este depende falhou (Deploys e status). Em superseded, um deploy mais novo tomou o lugar: acompanhe o mais novo.
5. Verificar a URL pública
Seção intitulada “5. Verificar a URL pública”Com ready, o serviço público responde em https://<slug>.<domínio de apps>, onde <slug> é o que você enviou na criação.
curl -sS -o /dev/null -w '%{http_code}\n' "https://meu-app-1760000000.nebularws.com/healthz"Se não for 200, leia os logs de runtime; para voltar à versão anterior, rollback. Para domínio próprio, Domínio próprio.
Listar o que você criou
Seção intitulada “Listar o que você criou”curl -sS "$NEBULAR_API/v1/projects" \ -H "Authorization: Bearer $NEBULAR_TOKEN"{"items": [{"id": "01j9zkk07fhzv5zsmn996cdwpb", "name": "meu-app-1760000000"}], "next_cursor": null, "has_more": false}