CLI Reference
Overview
Section titled “Overview”The Rebase CLI (rebase) manages your project from scaffolding to deployment.
Installation
Section titled “Installation”pnpm add -g @rebasepro/cliOr run any command without installing it: pnpm dlx @rebasepro/cli <command>.
Machine-readable output
Section titled “Machine-readable output”--json is the switch, and outside the cloud family it is the only one: rebase status, rebase resources, rebase apps list and rebase upgrade then put one JSON value on stdout — the result, or a {"error": {"message", "code", "hint", "issues"}} envelope with a non-zero exit — on every exit of the command, so a caller can parse stdout unconditionally. Without it they write human text and failures go to stderr. rebase cloud uses the same envelope and is the one exception to the switch: it also turns JSON on by itself when stdout is not a TTY, or when REBASE_JSON=1 is set. So rebase cloud status | cat is JSON while rebase status | cat is not — in a script, pass --json explicitly rather than relying on either rule.
Commands
Section titled “Commands”rebase init
Section titled “rebase init”Initialize a new Rebase project:
rebase init [directory]Sets up the project structure with frontend, backend, and shared packages.
| Flag | What it does |
|---|---|
-t, --template <preset> |
blog, ecommerce or blank. Default blog |
--headless |
Backend only — no admin panel and no collection files. --template has no effect, because there are no collections to seed |
-y, --yes |
Never prompt. Required wherever there is no terminal to answer, such as CI. It skips git init and dependency install — the interactive defaults say yes to both, so pass --git / --install if you want them |
-i, --install |
Install dependencies after scaffolding. Without it, init asks (default yes); under --yes, nothing is installed |
--no-install |
Install nothing, and do not ask |
-g, --git |
Initialize a repository and make the first commit. Without it, init asks (default yes); under --yes, no repository |
--database-url <url> |
Use an existing database instead of the managed one. Since 0.25 It must be a postgres:// or postgresql:// URL: anything else is refused before a file is written, and the interactive prompt asks again |
--introspect |
Generate collections from that database. Implies --template blank and needs --install |
--project <slug> |
Link the scaffold to a Rebase Cloud project |
--setup-key <key> |
The one-time key authenticating that link |
-a, --agent <name> |
Set up AI coding agents: the skills and the MCP server. Repeatable or comma-separated — claude, cursor, windsurf, gemini, codex, kiro, copilot or all. Without it, init asks and pre-ticks the agents installed on the machine; under --yes, none |
rebase dev
Section titled “rebase dev”Start the development server:
rebase devStarts both frontend and backend with hot reloading, and regenerates the Drizzle schema and the SDK types (generated/sdk/) on start and on every collection save. On 0.23 it regenerates the schema only, and rebase generate-sdk is yours to run.
Both ports are derived from the project’s path so several Rebase projects can run
side by side. Use the URLs rebase dev prints. Pin one with rebase dev --port 3001.
Since 0.25 An edit to .env
restarts the backend, which reads the file again, as the admin panel does. What
rebase dev sets itself is kept until you restart it: PORT, and
DATABASE_URL when it is on the managed database or on Docker. On 0.24 an .env
edit reached the backend only when some other watched file changed.
Since 0.25 With
--database-url, rebase dev first checks that something answers at that host
and port. If nothing does, it says so, naming the flag, and exits 1 with
nothing started; it also leaves the database .env names alone, never starting
its compose container. On 0.24 the backend’s banner said to verify
DATABASE_URL in .env, and dev kept running with the backend down.
rebase build
Section titled “rebase build”Build the project into a deployable bundle in dist-bundle/:
rebase buildThe bundle is the artifact you deploy — the runtime image loads it, so there is no application image to build yourself. Useful flags:
| Flag | Effect |
|---|---|
--output <dir> (or --out) |
Write the bundle somewhere other than dist-bundle/ (one app at a time) |
--vendor |
Always install and ship the bundle’s dependencies |
--no-vendor |
Never vendor; the pod installs on first start |
--skip-type-check |
Skip typechecking (faster, less safe) |
--no-static |
Do not fold the frontend into the backend bundle (each static app still gets its own bundle) |
--skip-static-build |
Fold the frontend as already built, without running its build command |
Dependencies are vendored by default so a pod restart does not pay a 35–55 second install. A tree that grows past 200 MB on disk is dropped instead, because the upload limit is 100 MB compressed — see the changelog for the reasoning.
Since 0.25 Every static app the
bundle carries gets a brotli (.br) and a gzip (.gz) copy beside each
content-hashed asset of 1 KB or more — the files a browser caches for a year.
The runtime sends the one the browser accepts, so those files cost no
compression work per request, the first visit after a deploy included, and
browsers that accept brotli get it (about 11% smaller than gzip on the admin’s
JavaScript). Your frontend’s own build output is left as it was; only the copy
in the bundle changes. On the example app this adds under a second to the build
and about 2.4 MB to the bundle.
rebase upgrade
Section titled “rebase upgrade”Move every @rebasepro/* package the project pins to one release, then
install with the package manager its lockfile names. rebase upgrade takes the
latest; --to 0.21.0 an exact version, with no registry lookup; --to canary a
dist-tag. Each pin in dependencies, devDependencies and
optionalDependencies, in every package.json under the project, keeps its ^
or ~, and nothing else in the file changes. peerDependencies and
workspace:, link:, file:, git and tag specs are listed and left alone.
Overrides in pnpm-workspace.yaml and package.json move too, but a link: or
file: override wins over every pin: it is reported, and
--drop-local-overrides removes it. --dry-run writes nothing, --no-install
skips the install, and --json prints one document.
Since 0.25 A project on a
prerelease, such as a canary, stays on its channel unless latest is newer. A
dist-tag that would move a pin backwards is refused (downgrade_refused under
--json) unless you pass --allow-downgrade; an exact --to is applied, and
flagged as a downgrade. A --dry-run of an upgrade the real run would refuse
shows what would move and exits 0, and under --json its document carries the
refusal as wouldRefuse ({ code, message, hint }). On 0.24, rebase upgrade
takes latest regardless.
rebase start
Section titled “rebase start”Run the built bundle the way a deployment runs it:
rebase startReads PORT and the rest of .env, unlike rebase dev, in the NODE_ENV they
set. A scaffolded .env says development, so the first account to register
still becomes the admin, and rebase start says so at the top; set NODE_ENV=production
for a production server. rebase start --bundle ./dist-bundle runs a bundle elsewhere.
rebase start starts no database, and the scaffold’s .env has no
DATABASE_URL, so a local run on the database rebase dev uses is
DATABASE_URL="$(rebase db url)" rebase start. Since 0.25
Without one, it prints that line before booting; on 0.24 the runtime stops on
“DATABASE_URL: is required”.
rebase apps list
Section titled “rebase apps list”Show the apps this repository declares:
rebase apps listA repository can declare more than one deployable app — a backend and a marketing
site, say. This is how you see what rebase build and deployment will act on.
rebase eject
Section titled “rebase eject”Take ownership of the server process and its image:
rebase ejectWrites the backend entrypoint and a Dockerfile into the project and flips its
backend over, so the repository builds its own image instead of running the
published runtime. From then on platform runtime upgrades no longer reach it,
and CORS, auth wiring, storage and shutdown become yours to configure.
Preview it with rebase eject --dry-run, which lists what would change and
changes nothing. --force replaces an existing backend/src/index.ts or
env.ts, keeping the current file as <name>.bak.
Since 0.25 The ejected server
keeps local uploads in the project’s uploads/, run from source or compiled, as
rebase dev does. It runs in backend/, so a relative STORAGE_PATH there is
read from backend/. On 0.24 the compiled server uses backend/dist/uploads,
which the next build deletes.
rebase schema generate
Section titled “rebase schema generate”Generate Drizzle ORM schema from your TypeScript collections:
rebase schema generateThis reads your collections from config/collections/ and generates backend/src/schema.generated.ts with Drizzle table definitions, enums, and relations.
rebase schema stale
Section titled “rebase schema stale”rebase schema stale # name what differs from the collections, exit 1 if anything doesrebase schema stale --fix # regenerate backend/src/schema.generated.ts insteadrebase dev keeps the file current by itself, so this is the check for a build
or a CI step. See Schema Generation.
rebase db
Section titled “rebase db”rebase db push # apply the collections to the database (development)rebase db generate # write a SQL migration filerebase db migrate # apply the pending migrationsrebase db url # print the connection string this project usesrebase db pull --from <url> # copy another database into the local onerebase db backup # pg_dump; db backups list and db restore beside itrebase db branch create <name> # a copy to work on; switch, list and prune it toorebase db stop # stop the managed development databaserebase db reset # delete it and start overEach is on Database Commands, with the order in which a
command picks its database; push, generate and migrate are covered in
depth on Schema Generation.
rebase console
Section titled “rebase console”Since 0.25
A Node REPL with the project’s data open, or one snippet run with -e:
rebase consolerebase console -e "rebase.dataAsAdmin.posts.find({ limit: 5 })"rebase (dataAsAdmin, sql, withAuth(user)) and collections are in
scope, and top-level await works. The database is resolved as rebase db push
resolves it, and opening it changes nothing in it. With -e, the awaited result
is printed as JSON on stdout and a throw exits 1. Flags: --eval/-e,
--collections/-c, --database-url, --docker. See
Scripts and the Console.
rebase apps init / rebase apps config
Section titled “rebase apps init / rebase apps config”rebase apps list # the apps this project declaresrebase apps init # write a rebase.json inferred from this projectrebase apps config <app> # the client configuration for one appapps init takes no name: it infers every app from the project and writes the
whole rebase.json, and refuses to replace one that exists unless you pass
--force.
Since 0.25 A name argument is
refused. --force keeps what inference cannot know from the file it replaces:
each app’s name, its path and its cms route. Everything else is inferred
again, and a declared app it no longer finds is named as removed, so on a
scaffold it writes back the rebase.json the scaffold shipped. The static
app’s build command uses the project’s package manager. On 0.24 the name is
ignored, and --force writes plain inference: the static app becomes web,
built with npm run build --workspace frontend.
Since 0.25 For a
checkout not linked to a Cloud project, apps config says it is not linked,
whether or not rebase dev is running, and gives the backend’s URL as
rebase dev serves it. On 0.24 it prints http://localhost:3001.
rebase status
Section titled “rebase status”Everything this project declares, and whether the environment actually binds it:
rebase status # every resource, and the variables it readsrebase status --json # machine-readable backend · managed Rebase's runtime boots your bundle declared in config/resources.ts configured by .env
buckets ✓ media s3 · account:minio ✓ S3_BUCKET__MEDIA ✓ S3_ACCESS_KEY_ID__MINIO (shared, for S3_ACCESS_KEY_ID__MEDIA) ○ exports s3 · S3_BUCKET__EXPORTS not set └ declared, not configured — uploads here answer 501 STORAGE_SOURCE_NOT_CONFIGUREDThree files decide what a backend can reach, and this prints all three together:
rebase.json says where your code is and who runs the server,
config/resources.ts says what the project needs, and the environment says how
to reach each thing. Everything else — rebase.resources.json, the bundle
manifest — is generated from the middle one for readers that cannot run your
code, and you never write it.
A ○ is the state worth knowing about before a deploy rather than after:
declared, not configured. A ✗ means the environment sets something wrongly,
which refuses the boot rather than degrading.
rebase resources
Section titled “rebase resources”What this project declares it needs — the databases, buckets, topics and queues its config code asks for, and the crons and functions its files define:
rebase resources # list themrebase resources --write # regenerate rebase.resources.jsonrebase resources --check # fail if the committed graph is stalerebase resources --json # machine-readablerebase resources --check is the flag a CI job uses to fail on a
rebase.resources.json that no longer matches the config code.
Since 0.25 --json combines
with it: on drift, rebase resources --check --json exits 1 with an error
envelope (resource_graph_missing or resource_graph_stale), and on success
the graph carries check.status. --write --json writes the file and reports
written.changed. On 0.24 --json printed the graph and ignored both flags.
A resource is declared in config code — database("analytics"),
bucket("media"), topic("signups"), queue("thumbnails") — or is a file
under backend/crons or backend/functions, and never written by hand in
rebase.resources.json, which is generated from those declarations so a host can
read what a project needs without building it. Each entry records who uses it
(collection:events, property:posts.cover, function:report).
A backend also has a default database and a default storage source that nobody
declares. Both are listed here, marked implicit, and neither is written to
rebase.resources.json — the host supplies them, so recording them would ask
for something to be provisioned that nobody asked for.
To see what the platform holds for a project against what its code declares,
and to remove a provisioned database the code no longer names, see
rebase cloud resources.
rebase cloud — every command for Rebase Cloud: auth, project link, deploys, config, databases, resources, compute and more — has its own page.
rebase generate-sdk
Section titled “rebase generate-sdk”Generate a typed SDK from your collection definitions:
rebase generate-sdkCreates TypeScript types and a type-safe client for all your collections.
--register also types server-side data: it writes the types and the
Register declaration to config/generated/, and remembers the choice in
rebase.json (registerDatabase), so later runs, rebase dev and fresh clones
keep them current.
See Typed server-side data.
rebase doctor
Section titled “rebase doctor”rebase doctorThe command to run when something is wrong and you do not yet know what. It reports and never changes anything, so it is safe against any database you can reach.
Without a database. These run first, because everything that stops a project from working at all happens before a table can be compared:
| Check | Why |
|---|---|
| Node version | Against the range the CLI declares. Too old is not reported as “unsupported Node” — it is a syntax error inside a dependency. |
| Package managers | Two lockfiles in one project. npm install in a pnpm workspace rewrites node_modules into a layout pnpm disagrees with, and the symptom is Cannot find module hours later. |
| Duplicate slugs | The registry keeps the last collection registered, so the other one is not reported missing — it is served as the winner, under its own name. |
.env sanity |
A JWT_SECRET shorter than 32 characters (which production refuses to boot on), and NODE_ENV=production with neither CORS_ORIGINS nor FRONTEND_URL. Values are never printed. |
@rebasepro/* version skew |
The same package pinned to different versions across the project’s package.json files. Two copies break instanceof between them, which fails as a type guard rejecting its own type. |
| Connection strings | An unencoded = in a URL parameter, which PostgreSQL’s own tools refuse to parse — so backups and psql break while the app keeps working. |
| Custom functions | What each function needs from its host, and which of them would not run on an edge runtime. |
Against the database, when DATABASE_URL is set:
| Check | Why |
|---|---|
| Collections → generated schema | Whether schema.generated.ts is stale. |
| Collections → database | Missing tables, columns, enums, foreign keys and junctions. |
| Required extensions | A { type: "vector" } property needs pgvector, which Rebase installs only where a project declared it. |
| Schema stamp | Whether this database was provisioned from these collections. A hash, so it can say the two disagree and never which is ahead. |
| Collections → SDK types | Whether the generated typed SDK is stale. |
| RLS policies | Whether the database’s policies match the securityRules you declared, and whether any policy names a role this server cannot use. |
If the database is unreachable, its phases are reported as skipped with the reason and the rest still runs — see Troubleshooting.
Exits non-zero when a check finds an error, or when a phase could not run
because the database it was given refuses connections. A phase skipped because
you set no DATABASE_URL is not a failure.
rebase doctor --policies runs only the RLS checks — no schema diff, no SDK
types — and fails closed, which makes it the form to use as a CI gate against a
deployed database.
rebase auth
Section titled “rebase auth”Authentication management commands:
rebase auth reset-password --email admin@example.com --password NewPassword123!rebase api-keys
Section titled “rebase api-keys”Manage the project’s service API keys — the credential an agent, script or another service uses, as opposed to an end user’s session:
rebase api-keys listrebase api-keys get abc123-def456rebase api-keys create --name "Blog CI" --scopes data:read:posts,data:write:posts --expires-in 90rebase api-keys create --name "Ops" --full-access --roles admin --expires-at 2027-01-31rebase api-keys revoke abc123-def456--scopes names what the key may do, comma-separated or repeated; --full-access
gives it every scope the service key holds except keys:*. --roles adds RLS
roles beside service, --expires-in takes days and --expires-at an ISO date.
rebase api-keys scopes lists every scope the backend knows. A key is shown once.
The key’s scopes and the row-level security of the identity it acts as both apply.
See API keys.
rebase storage
Section titled “rebase storage”Since 0.25 Whole-store
maintenance of stored files, through the running backend with
REBASE_SERVICE_KEY from .env:
rebase storage orphans # files no row namesrebase storage orphans --source media --delete # and delete them, after askingrebase storage move --from "(default)" --to media --prefix avatarsrebase storage move --from "(default)" --to media --prefix avatars --delete-sourceorphans reads every row of every collection, soft-deleted rows included, and
lists the files of one source (--source, the default source when left out)
that no string in any row names: a key, a URL, a rebase-storage: reference in
a text. Files newer than --older-than hours (24 by default) are left out,
because the admin panel uploads a file before the row that names it is saved.
--delete asks before it deletes (--yes skips the question, and is required
with no terminal), the server finds the files again first, and nothing is
deleted while a collection cannot be read. Files that only your own tables
outside collections name look unused: check before deleting.
move copies the files under --prefix to another source under the same keys,
a page at a time. A file the target already holds at the same size is skipped,
so a stopped move resumes by running it again; --delete-source deletes each
file from the source once the target holds it. Both need database:read, and
deleting or moving database:write, which the service key holds.
rebase skills install
Section titled “rebase skills install”Install the Rebase reference skills for your AI coding assistants — every --agent above:
rebase skills installrebase skills install --agent claude,cursorrebase skills install --agent allSee Agent Skills for the full list and where files are written.
rebase telemetry
Section titled “rebase telemetry”Anonymous usage sharing, off until you say yes. An interactive rebase init
asks at the end, and the prompt defaults to no, so nothing is shared unless you
answer yes. The answer is per machine (~/.rebase/telemetry.json): after a yes
init never asks again, and after a no the next init asks again. Each
checkout gets a random project id, kept in its gitignored
.rebase/telemetry-project.json. Since 0.25
On 0.24 the prompt defaults to yes, and the project id changes on every
rebase dev restart.
rebase telemetry statusrebase telemetry showrebase telemetry enablerebase telemetry disablestatus prints the current setting, show prints exactly what would be sent —
whether or not sharing is on, so you can read the payload before deciding — and
the other two change it. If you never ran init, nothing was ever collected.
Next Steps
Section titled “Next Steps”- Schema Generation — The migration workflow, from collection edit to production
- Schema as Code — How schema generation works
- Quickstart — Get started