Deploying Grit apps
Orbita is a general-purpose PaaS and needs no framework. This page is for people using the Grit framework — because we own Grit too, Orbita recognises those apps and gives them a zero-config fast path. It's the same idea as Nixpacks being a fast path for Rails: a recognised app type, not a requirement.
Why Grit apps are special
A Grit app has a known shape, declared in grit.json at the repo root. Orbita reads it and
derives everything else: how many services, what to build, from which context, on which port,
which database, and when to migrate. You write no Dockerfile and no build config.
Everything below is derived — you never type it:
From grit.json | Orbita derives |
|---|---|
architecture: single | 1 container — one Go binary with the SPA embedded, on :8080 |
architecture: api | 1 container — the Go API on :8080 |
architecture: double | 2 containers — api + web |
architecture: triple | 3 containers — api + web + admin (+ docs if present) |
mobile is rejected; expo and desktop are never deployed.
It does not generate a Dockerfile and does not fall back to Nixpacks for Grit apps. Grit
ships correct, multi-stage Dockerfiles; Orbita builds those with the right contexts (the API
from apps/api, Next.js apps from the repo root with NEXT_PUBLIC_API_URL baked from your API
domain).
The manifest
You write this once. See the full spec.
app: rental
repo: MUKE-coder/rental
branch: main
addons: [postgres, redis]
domains:
web: rental.example.com
admin: admin.rental.example.com
api: api.rental.example.com
migrate: true
env:
from: .env.production # encrypted into Orbita, never committedDeploy it
First, make sure the CLI knows your server. If you ran orbita init, it's already registered.
Otherwise connect to it once:
orbita login https://orbita.example.com # once — registers the host as "prod"Then, from the project directory (the one with grit.json):
orbita github-auth # once — store a GitHub token (repo + admin:repo_hook)
orbita deploy --host prodNo orbita.yaml yet? A first-run wizard generates one from the detected app shape.
Preview without changing anything:
orbita deploy --plan --host prod▸ Plan (dry run — nothing will be changed)
App: rental
Mode: triple
Migrate: true
Addons: postgres, redis
create rental-api → api.rental.example.com
create rental-web → rental.example.com
create rental-admin → admin.rental.example.comWhat happens, in order
- Detect —
grit.jsonat the repo root marks it a Grit app;architecturepicks the strategy. - Resolve the host — API URL +
orb_token from~/.orbita/hosts.yaml. - Ensure the repo — create it private if missing, then push.
- Reconcile — org, project, environment, addons, env + secrets, domains. Idempotent.
- Build — each service from the Dockerfiles Grit ships.
- Migrate —
cmd/migratein a one-off container, under a Postgres advisory lock. - Cut over — only if the migration succeeded. Previous images are kept for rollback.
Migrations gate the cutover
Orbita runs migrations before cutover, under an advisory lock so two concurrent deploys can't race. Non-zero exit = deploy stops and the previous version keeps serving. You never cut over to a schema-mismatched image.
The most common failure: go.sum isn't committed, so go run ./cmd/migrate can't resolve
modules. Commit it — real Grit apps ship it.
orbita logs --host prod --service migrateBatteries included
Mounted on the API by default:
- Pulse — latency, SQL, errors →
https://api.rental.example.com/pulse/ui - Sentinel — WAF, rate limiting, anomalies →
https://api.rental.example.com/sentinel/ui - GORM Studio — off by default; it edits live data. Enable with
studio: true.
Not using Grit?
Everything still works — Orbita just takes a different path. See deploying your first app for the Docker image, Compose, Dockerfile, and Nixpacks routes, and the orbita.yaml spec for how the build path is chosen.