Getting started
Run backd with docker compose, or build it from source.
Core concepts:
realm realm.yaml settings; shared user pool with single sign-on
└─ database one application's data → MongoDB database <realm>__<database>
└─ collection documents validated by schema.json → one MongoDB collection
└─ document JSON object with server-owned id and _meta
Quick start with docker compose
The repository includes a docker-compose.yml that runs backd and MongoDB with the sample config in examples/config, together with this documentation site and the JavaScript client’s example apps. nginx serves all of them over HTTPS on one origin, https://localhost:8443. Plain http://localhost:8080 redirects there. It also runs the functions executor, egress and (folded into backd) a worker: the blog realm’s sample stats function calls the executor for real (curl https://localhost:8443/v1/blog/main/_func/stats).
| Path | Serves |
|---|---|
/healthz, /readyz, /v1/… | backd’s API |
/example/ | The example apps: a blog and a shared-expenses app |
/, /docs/… | This documentation site, reloading as you edit docs/ |
The sample config:
examples/config/
├── shop/
│ ├── realm.yaml
│ ├── orders/
│ │ ├── items/{schema.json, indexes.json}
│ │ └── clients/{schema.json, indexes.json}
│ └── billing/
│ └── invoices/{schema.json, indexes.json}
├── blog/
│ ├── realm.yaml
│ └── main/
│ └── posts/{schema.json, indexes.json, collection.yaml}
├── expenses/ (and expenses-with-functions/: the same app with server-side functions)
│ ├── realm.yaml
│ └── main/
│ ├── groups/{schema.json, indexes.json, collection.yaml}
│ └── expenses/{schema.json, indexes.json, collection.yaml}
└── workshop/ the functions cookbook: sync, jobs, cron and a webhook
├── realm.yaml
└── main/
├── orders/, refunds/, receipts/, reports/, digests/, events/
└── _functions/{order_total, refund, refund_receipt, export_orders, nightly_cleanup, daily_digest, payment_webhook}/
make example
export CURL_CA_BUNDLE=$PWD/docker/certs/ca.crt # in another terminal: curl trusts the local CA
curl https://localhost:8443/readyz
The first start builds the images and downloads the docs theme, which takes a few minutes; later starts are quick. To start the stack without make example, create the certificates first: scripts/local-certs.sh && docker compose up --build.
HTTPS with a local CA
Like a production deployment, the local stack only serves HTTPS. Its certificate comes from a local certificate authority that make example manages with certsfor, run as a container (scripts/local-certs.sh):
- First run: it creates the CA and a certificate for
localhost,127.0.0.1and::1, from the templates indocker/cfd/. - Every run: it asks certsfor for the certificate again, which renews it when less than 20% of its 90-day lifetime is left. Restarting
make exampleis enough to keep it valid. - Files: everything goes to
docker/certs/, which git ignores:ca.crt(the CA certificate),localhost.crtandlocalhost.key(nginx’s certificate and key) and certsfor’s database with the CA’s key.
Browsers warn about the certificate until you trust the CA. Either accept the warning once for https://localhost:8443, or add docker/certs/ca.crt to your trust store. make example prints the commands for your system when the CA isn’t trusted yet:
| Where | How |
|---|---|
| Debian, Ubuntu | sudo cp docker/certs/ca.crt /usr/local/share/ca-certificates/backd-local-ca.crt && sudo update-ca-certificates |
| Fedora, RHEL | sudo cp docker/certs/ca.crt /etc/pki/ca-trust/source/anchors/backd-local-ca.crt && sudo update-ca-trust |
| macOS | sudo security add-trusted-cert -d -r trustRoot -k /Library/Keychains/System.keychain docker/certs/ca.crt |
| Windows | certutil -addstore -f ROOT docker\certs\ca.crt (as administrator) |
| Firefox | Settings → Privacy & Security → Certificates → View Certificates → Authorities → Import |
| Chrome or Chromium on Linux | certutil -d sql:$HOME/.pki/nssdb -A -t C,, -n "backd local CA" -i docker/certs/ca.crt |
| curl | --cacert docker/certs/ca.crt, or export CURL_CA_BUNDLE=$PWD/docker/certs/ca.crt |
To start over, remove the CA from your trust store and delete docker/certs/; the next run creates a new one. The local stack doesn’t send Strict-Transport-Security, because browsers would apply it to every site on localhost.
Create, read, update and delete a document:
curl -X POST https://localhost:8443/v1/shop/orders/items \
-H 'Content-Type: application/json' \
-d '{"name": "Widget", "price": 12.5, "stock": 3}'
# → 201 {"id":"darf3h9ck12g00eg2hh0","_meta":{...},"name":"Widget",...}
curl https://localhost:8443/v1/shop/orders/items/darf3h9ck12g00eg2hh0
curl -X PATCH https://localhost:8443/v1/shop/orders/items/darf3h9ck12g00eg2hh0 \
-H 'Content-Type: application/merge-patch+json' -d '{"stock": 2}'
curl -G https://localhost:8443/v1/shop/orders/items \
--data-urlencode 'where={"name": {"$ilike": "wid%"}, "price": {"$lt": 20}}' \
--data-urlencode 'order_by=-price' -d count=true
curl -X DELETE https://localhost:8443/v1/shop/orders/items/darf3h9ck12g00eg2hh0
The shop realm has auth: disabled, so these requests need no credentials.
The blog realm keeps the default auth: enabled, and its posts have access rules: anyone can read published posts, and only signed-in users can write. A server-side service writes with an API key, which bypasses the rules:
Keys are created by the realm’s administrators. Create the first one with backd bootstrap, log in as them with the CLI in the container, and create a key:
docker compose exec backd /backd bootstrap --realm blog --email ops@example.com # asks for a password
docker compose exec backd /backd login --realm blog --url http://localhost:8080 --email ops@example.com
KEY=$(docker compose exec -T backd /backd apikey create --realm blog --name demo --expires 1d)
curl -X POST https://localhost:8443/v1/blog/main/posts -H "Authorization: Bearer $KEY" \
-H 'Content-Type: application/json' -d '{"title": "Hello", "published": true}'
curl https://localhost:8443/v1/blog/main/posts # anonymous: published posts only
To see web apps use these realms, open the example apps at https://localhost:8443/example/: a blog on the blog realm, and a shared-expenses app on the expenses realm.
Releases
Each tagged version is published automatically:
- binaries for Linux, macOS and Windows (amd64 and arm64), with a
checksums.txt, on the repository’s GitHub Releases page; - a container image for linux/amd64 and linux/arm64:
ghcr.io/fernandezvara/backd:<version>, andlatestfor the newest stable version.
Each release is checked and verifiable:
- archives come with
checksums.txtand an SPDX SBOM each (*.sbom.json); - the image is scanned for known vulnerabilities before it’s published, carries SBOM and provenance attestations, and is signed with cosign using GitHub’s identity (no keys to manage). Verify it with:
cosign verify ghcr.io/fernandezvara/backd:v0.8.1 \
--certificate-identity-regexp '^https://github.com/fernandezvara/backd/\.github/workflows/release\.yml@refs/tags/v' \
--certificate-oidc-issuer https://token.actions.githubusercontent.com
backd version prints the version of a binary or image.
Building from source
backd needs Go 1.27.1 or later.
git clone https://github.com/fernandezvara/backd
cd backd
make build # produces bin/backd
Or build the container image:
docker build -t backd .
Commands
backd on its own, backd help and backd --help print the commands grouped by who uses them (develop, session, administer a realm, server); backd help <command> and backd <command> --help show one command’s options. Nothing starts without a command: a server is backd serve.
| Command | Purpose |
|---|---|
backd serve | Load config, provision or verify MongoDB, serve HTTP |
backd serve --with-worker | Also run the worker role (below) in the same process |
backd worker | Claim and run async jobs (mode: async); WORKER_CONCURRENCY caps how many run at once |
backd provision | Load config, apply provisioning once and exit |
backd template realm --realm <realm> [--sample] | Create a commented realm.yaml (and, with --sample, a sample database) |
backd template database --realm <realm> --database <database> [--sample] | Create a database directory (and, with --sample, a sample collection) |
backd template function --realm <realm> --database <database> --name <name> | Create a function in the database’s _functions project |
backd template email-capture --realm <realm> --database <database> | Add a development email function that stores each email in an outbox collection |
backd template project --dir <directory> --realm <realm> | Create a complete config repository: realm, sample database and function (with tests), Dockerfiles, a local dev stack and CI (see Starting from a template) |
backd functions build [--check] | Bundle the functions in CONFIG_DIR with Deno (see Functions) |
backd functions invoke|history|logs|jobs --function <realm>/<database>/<name> | Call a function through a running backd, and see its recent calls, its console output and its async and scheduled jobs (see Calling functions, Async jobs) |
backd executor | Run server-side functions for backd (needs Deno; the backd-executor image has it) |
backd egress | Run the egress role, the only way a function process reaches the network |
backd config check, backd config fingerprint | Validate CONFIG_DIR as startup would, or print its fingerprint (see Deploying config) |
backd rules test | Run each collection’s rules.test.yaml against its rules:, without a database (see Testing rules) |
backd version | Print the version |
backd databases [--collections] | Print the MongoDB databases (or collections) the config uses, one per line (needs only CONFIG_DIR; see least-privilege deployments) |
backd bootstrap --realm <realm> --email <email> | Create a realm’s first administrator (see Command-line administration) |
backd login --realm <realm>, logout, whoami | Sign in to a running backd as an administrator and keep the session for the commands below |
backd user <command> --realm <realm> … | Manage a realm’s users through the admin API (see Users and passwords) |
backd apikey <command> --realm <realm> … | Manage a realm’s API keys through the admin API (see API keys) |
Your own config
Start a config tree from templates, then start backd on it:
backd needs MongoDB 8.2 as a replica set; a single node is fine. MongoDB shows how to start one.
mkdir config && export CONFIG_DIR=$PWD/config
backd template realm --realm demo --sample
export MONGO_URI='mongodb://localhost:27017/?replicaSet=rs0' # a single-node replica set
backd provision
backd bootstrap --realm demo --email me@example.com # the first administrator; asks for a password
HTTP_ADDR=127.0.0.1:9090 backd serve &
backd login --realm demo --url http://127.0.0.1:9090 --email me@example.com
KEY=$(backd apikey create --realm demo --name local --expires 1d)
curl http://127.0.0.1:9090/v1/demo/main/posts -H "Authorization: Bearer $KEY"
backd itself serves plain HTTP (on :8080 by default; here 9090, to stay clear of the local stack). Put TLS in front of it as in the production reference.
See Data model for details. See Operations for provisioning and Documents for the REST endpoints.