Instances from the CLI

One command line, no repository, no code. Instances are things you make when you need one and delete when you do not, locally or in your own Cloudflare account, with the same commands for each.

bun i -g @voidbase-cloud/voidbase      # or: npm i -g @voidbase-cloud/voidbase
voidbase version

Local instances

This is the same job the standalone executable does, for people who would rather install from npm than download a binary. The difference is that it remembers what you have made, so a second instance does not mean finding where you put the first.

Local means wherever the CLI is running. That is usually a laptop, but it is just as often a container in a compose file, a CI job that needs a real backend for its tests, or a desktop app that ships voidbase inside it. Nothing here talks to Cloudflare and none of it needs an account.

voidbase local new blog
created "blog" in ~/.voidbase/instances/blog (pb_hooks/main.pb.js, .gitignore, pb_secrets/main.ts)
superuser admin@example.com / k3f8s2m1qzA1

next: voidbase local start blog   (the API on 8090, the panel at http://127.0.0.1:8090/_/)

The instance is a directory holding its own database, hooks and configuration, and a row in ~/.voidbase/instances.json recording its name and port. Nothing is sent anywhere. Pass --dir to put it where you want it, --port to pick the port, and --email with --password to choose the superuser rather than have one generated.

voidbase local ls
3 local instance(s), from ~/.voidbase/instances.json:
  blog                 :8090   running      2 MB  ~/.voidbase/instances/blog
  shop                 :8091   stopped    941 kB  ~/.voidbase/instances/shop
  workshop             :8092   stopped     14 MB  ~/.voidbase/instances/workshop

Each instance keeps the port it was given, so they never collide and the address stays the same between runs. voidbase local start blog runs one, and with no name it starts the first.

voidbase local rm workshop            # forget it, keep the data
voidbase local rm workshop --purge    # and delete the directory, after typing the name back

Removing an instance forgets it. The directory stays where it is until --purge says otherwise, and that asks you to type the name back before deleting a database.

A local instance is the same shape as a project: the same pb_hooks, pb_migrations and pb_secrets. Moving from one to the other is moving a directory into a repository, so nothing you write here is throwaway.

On the internet while it runs

--tunnel puts the instance on a Cloudflare quick tunnel: the banner gains a Tunnel: https://<words>.trycloudflare.com line, that address reaches the API and the panel over HTTPS, and the tunnel closes with the server. Every start gets a fresh address, so it is for showing work rather than hosting it. It runs cloudflared from VOIDBASE_CLOUDFLARED, from your PATH, or downloaded once into ~/.cache/voidbase/cloudflared/; without one the server serves as usual and says so.

voidbase local start blog --tunnel     # the same instance, on https://<words>.trycloudflare.com while it runs
voidbase serve --tunnel --dev          # in a project: the watcher keeps one tunnel across hook restarts

On Cloudflare's runtime, on this machine

voidbase serve runs the instance on Bun. --workers runs it on workerd, so what you exercise is the Workers code with the bindings a deploy wires up: it generates the very project voidbase deploy would upload and runs it with Void's dev server, with D1, R2, the jobs queue and the realtime hub in Miniflare, persisted under .cloud/<name>/.void/. No token, no account, nothing reaches Cloudflare. The first start takes half a minute or so; cron triggers do not tick locally.

voidbase serve --workers               # the project "voidbase deploy" would upload, on workerd, under .cloud/<name>
voidbase serve --workers --database durable

On your Cloudflare account

The same idea, one command further. Everything below needs one API token, and one command prints the link that creates it with the right permissions already selected.

voidbase token                        # prints the link that creates the token
export VOIDBASE_DEPLOY_CF_API_KEY=...   # paste it here, or into pb_secrets/secrets.json as a local() key

Create one

Run this in an empty directory. It is not a project and it does not become one.

voidbase deploy --name blog-api
voidbase deploy --name blog-api --domain api.example.com

That creates the Worker, its database, its file storage, its job queue and its realtime object, deploys the server into it, and prints the address. With a hostname you own on the same account, name it and the domains plugin attaches it, waits for the certificate and turns workers.dev off. --database durable keeps the data in a SQLite-backed Durable Object instead of D1. Re-running is idempotent: what exists is reused.

The first superuser is created with a generated password, saved to pb_data/.superuser-credentials in the directory you ran the command from. That file is the one thing the command leaves behind, so keep it or sign in and change the password.

See what you have

voidbase instances
3 instance(s) on Example Ltd:
  blog-api                         release 1.0.0          updated 2026-09-14
  staging-api                      release 1.0.0          updated 2026-09-14
  workshop-demo                    release 0.9.0-beta.69  updated 2026-09-13

Delete one

This takes the Worker and everything it owns: the database and its contents, the bucket and its files, the queue, and any custom domain pointing at it. It prints that list and waits for the name to be typed back, and in a script it refuses unless --yes says you already decided.

voidbase destroy workshop-demo

There is no undo and no backup is taken. Take one from the admin panel's Backups screen first if the data matters. voidbase deploy --remove is the gentler command: it deletes the Worker and leaves the database, the bucket and the queue for the next deploy to find.

Moving data between them

A local instance, a tunnelled one and one on Cloudflare are the same server, and migrate moves the data between any two that are running, in either direction: a backup taken on the source through the backups API and restored on the target, so the target's collections, records, files, settings and superusers become the source's. Every step is printed, --dry-run signs in on both sides and stops, and --keep leaves the archive on both sides.

voidbase migrate http://127.0.0.1:8090 https://blog-api.example.workers.dev \
  --from-email admin@example.com --from-password k3f8s2m1qzA1 \
  --to-email you@example.com --to-password ...          # --dry-run first, if you like

Updating

voidbase update

A global install reinstalls itself at the newest published version. Your local instances are directories of data and configuration, not copies of the program, so they are all on the new version the next time you start one. Nothing is migrated and nothing is lost.

Instances in your Cloudflare account are the other case. Those run a copy of voidbase that was uploaded when you created them, so updating the CLI changes what the next deploy would carry, not what is answering requests. voidbase deploy from the same directory puts the new version live.

voidbase update --check changes nothing. It prints the version you are on and the newest one, and exits 1 when you are behind and 2 when it could not find out, which is what a pipeline reads. --to 1.0.0 goes to a particular version rather than the newest, and --dry-run prints the command it would run without running it.

You do not have to remember to check. Any voidbase command mentions a new release once a day, from an answer cached under ~/.voidbase. It never speaks in CI or when the output is not a terminal, and VOIDBASE_NO_UPDATE_CHECK=1 silences it everywhere. Every release is written up in the release notes.

When this stops being enough

The moment you want an endpoint of your own, a handler that runs when a record changes, or a schema you keep in version control rather than clicking into the panel, you want a voidbase project. It is the same instance with a repository around it, and voidbase init --template <name> starts one from somebody's working project.

Plugins

Plugins exist and the marketplace lists them. voidbase plugins add <name> installs one: its files at the commit the marketplace approved land in pb_plugins/<name>, and voidbase.lock pins the marketplace, the version, that commit and a hash of the files. The instance loads it beside the eight plugins voidbase ships, and a superuser reads where each came from at /api/plugins. Removing a shipped plugin turns it off; installing one with its name takes its place. The plugins page has what a plugin is, the shipped ones and their knobs, and the installer.

A local instance and a project are the same shape, a directory, so the command runs there as it does in a project: it changes pb_plugins/ and voidbase.lock in place, and the instance verifies every plugin against the lockfile and loads them when it restarts.

voidbase plugins ls                  # in the instance's directory
voidbase plugins add echo
voidbase plugins update
voidbase plugins remove echo

Any marketplace serving the registry protocol works, ours being the default: --marketplace <url> on an install, or VOIDBASE_PLUGIN_MARKETPLACES. To publish one, a repository with a manifest.json is enough: submit it and the marketplace audits it and records the commit.

Found something wrong on this page?

Fix it yourself. The link below opens this file in GitHub's editor and forks the repository for you if you need one, and your change becomes a pull request without leaving the browser.