Standalone executable

One file that contains the server, the admin panel, the system migrations and the hook typings. Nothing is installed, and this is the one page here that needs neither npm nor bun.

Download it

Builds are published for Linux, macOS and Windows on both amd64 and arm64, with musl builds for Alpine, on the releases page, as voidbase_<version>_<os>_<arch>.zip. On Linux:

# pick the build for this machine: linux_amd64, linux_arm64, or linux_amd64_musl on Alpine
VERSION=1.0.0
curl -LO https://github.com/voidbase-cloud/voidbase/releases/download/v${VERSION}/voidbase_${VERSION}_linux_amd64.zip

unzip voidbase_${VERSION}_linux_amd64.zip
chmod +x voidbase

On macOS, with the Apple Silicon build and the quarantine flag cleared:

VERSION=1.0.0
curl -LO https://github.com/voidbase-cloud/voidbase/releases/download/v${VERSION}/voidbase_${VERSION}_darwin_arm64.zip
unzip voidbase_${VERSION}_darwin_arm64.zip
chmod +x voidbase
xattr -d com.apple.quarantine voidbase   # macOS blocks downloaded binaries until you say otherwise

Start it

Make the first account, then run the server. The password wants eight characters or more.

./voidbase superuser upsert you@example.com your-password
./voidbase serve

It prints where it is:

Server started at http://127.0.0.1:8090
├─ REST API:  http://127.0.0.1:8090/api/
└─ Dashboard: http://127.0.0.1:8090/_/

Open the dashboard, sign in with the account you just made, and you have an instance. Design a collection there and it is immediately available over the API, which is what the SDK page connects to, and described at /api/docs.

Where the data is

Everything the instance owns lives in a pb_data/ directory beside the executable: the database, the uploaded files, and the generated typings that make editing hooks autocomplete. Copy that directory and you have copied the instance; delete it and you have a new one. There is more about it under pb_data.

On the internet for a minute

To show someone what you made without a server, a domain or a certificate, a Cloudflare quick tunnel does it for as long as the server runs:

./voidbase serve --tunnel
Server started at http://127.0.0.1:8090
├─ REST API:  http://127.0.0.1:8090/api/
├─ Dashboard: http://127.0.0.1:8090/_/
└─ Tunnel:    https://<words>.trycloudflare.com

That address reaches the API and the dashboard over HTTPS until you stop the server, and the next start gets a fresh one, so it is for showing work rather than hosting it. It needs cloudflared: the one VOIDBASE_CLOUDFLARED names, else the one on your PATH, else the release binary is downloaded once into ~/.cache/voidbase/cloudflared/. When none can be had, the server still starts and says so in one line.

On a server

By default it listens on the loopback address only. To take connections from other machines:

./voidbase serve --http 0.0.0.0:8090

Put something that terminates TLS in front of it before doing that on the open internet. A reverse proxy (Caddy, nginx, Cloudflare Tunnel) is the usual answer, and the panel's Settings page has a trusted-proxy setting so the instance sees the real client address rather than the proxy's.

To keep it running, a systemd unit is enough:

Linux
# /etc/systemd/system/voidbase.service
[Unit]
Description=voidbase
After=network.target

[Service]
Type=simple
User=voidbase
WorkingDirectory=/opt/voidbase
ExecStart=/opt/voidbase/voidbase serve --http 127.0.0.1:8090
Restart=always
RestartSec=5

[Install]
WantedBy=multi-user.target
sudo systemctl daemon-reload
sudo systemctl enable --now voidbase
sudo systemctl status voidbase

Moving the data somewhere else

An instance made here is not stuck here. migrate takes a backup on one running instance and restores it on another, over HTTP, whichever way each of them runs: this executable, the npm package, or a Worker on Cloudflare, in either direction. The target's collections, records, files, settings and superusers become the source's. --dry-run signs in on both sides and says what would happen.

./voidbase migrate http://127.0.0.1:8090 https://blog-api.example.workers.dev \
  --from-email you@example.com --from-password ... --to-email you@example.com --to-password ...

Updating

./voidbase update --backup

This fetches the newest release for your platform, checks it against the published checksum and replaces the executable in place. --backup zips pb_data first, which is worth the second it costs.

Your data is not touched. Everything the instance owns is in pb_data/, and an update replaces only the program that reads it. Restart the server and you are on the new version.

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.

Adding your own behaviour

Put JavaScript in a pb_hooks/ directory beside the executable and it is loaded at startup: new endpoints, handlers that run around writes, scheduled work. That is the same directory a project has, and pb_hooks is the guide to it. When you want that code in a repository rather than beside a binary, a directory the binary serves is the next page, and a voidbase project the one after it.

What it cannot do

Reach Cloudflare. deploy, sync, init, serve --workers and the rest of the toolchain come with the npm package, and the executable says so and prints the bunx @voidbase-cloud/voidbase command to run instead. Everything that talks to a running instance over HTTP works from here: migrate, types, plugins, import, export and the voidbase cloud commands.

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.

The executable is the filesystem case: the command changes pb_plugins/ and voidbase.lock beside it, and the instance verifies every plugin against the lockfile and loads it when it restarts. There is no build of yours to wait for and no node_modules: a plugin's imports resolve to the modules the executable is already running.

./voidbase plugins ls                # what this instance runs, and where each plugin came from
./voidbase plugins add echo          # into pb_plugins/echo beside the executable, pinned in voidbase.lock
./voidbase plugins update            # bring them all up to date
./voidbase plugins remove echo       # remove it; for a plugin voidbase ships, turn it off

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.