Storage backends
Nimbus selects its tenant storage backend with the --tenant-provider flag
on nimbus start. Tenants stay isolated at the storage level on every
backend, and clients connect the same way regardless of which one you pick —
the choice is purely a deployment decision.
This page shows how to run each backend. For the full flag ↔ environment variable ↔ config-key table, see the configuration reference.
Choose a backend
Section titled “Choose a backend”| Backend | --tenant-provider | External service? | Pick it when |
|---|---|---|---|
| SQLite | sqlite (default) | No | Local development and single-node production with zero setup |
| Postgres | postgres | Yes | You already run PostgreSQL infrastructure |
| MySQL | mysql | Yes | You already run MySQL infrastructure |
| libSQL / Turso | libsql-replica | Yes | You want local replica reads against a remote libSQL primary (typically Turso) |
| redb | redb | No | You specifically need the retained embedded key-value backend; prefer SQLite otherwise |
Provision tenants
Section titled “Provision tenants”Nimbus provisions tenants through the selected backend before serving the request that needs them. Automatic tenant creation and protocol adapters await the complete provider lifecycle, including opening an already-existing durable tenant into the current process. A retry after an interrupted create therefore does not report success until the tenant runtime is ready.
Provider-reachable background work follows the same boundary. For example, each DynamoDB TTL sweep admits or opens its configured tenants sequentially before entering the synchronous sweep core; one tenant’s admission or sweep failure is reported without preventing later tenants in that pass.
The Rust Engine::create_tenant blocking API is embedded-only and accepts
SQLite or redb compositions. Embedders using PostgreSQL, MySQL, or libSQL must
retain an Arc<Engine> and use create_tenant_async for explicit creation or
ensure_tenant_ready_async for idempotent request admission. Calling the
blocking API on an external-provider composition fails immediately with an
actionable error instead of surfacing later during traffic.
Run on SQLite (default)
Section titled “Run on SQLite (default)”No flags needed — nimbus start uses embedded SQLite with one database file
per tenant in the data directory (<data-dir>/<tenant-id>.sqlite3):
nimbus start --data-dir ./data# Equivalent to: nimbus start --data-dir ./data --tenant-provider sqliteThe data directory defaults to ./data when --data-dir is omitted.
Run on Postgres
Section titled “Run on Postgres”Point Nimbus at an existing PostgreSQL instance. Each tenant gets an isolated schema:
nimbus start \ --tenant-provider postgres \ --postgres-url "postgresql://user:pass@db.example.com:5432/nimbus"--postgres-url is required. Optional flags:
--postgres-metadata-schema— cross-tenant metadata schema (defaultnimbus_provider).--postgres-tenant-schema-prefix— per-tenant schema name prefix (defaulttenant_).--postgres-min-connections/--postgres-max-connections— pool sizing; the pool’s built-in sizing applies when unset.
Run on MySQL
Section titled “Run on MySQL”Point Nimbus at an existing MySQL instance. Each tenant gets an isolated database:
nimbus start \ --tenant-provider mysql \ --mysql-url "mysql://user:pass@db.example.com:3306/nimbus"--mysql-url is required. Optional flags:
--mysql-metadata-database— cross-tenant metadata database (defaultnimbus_provider).--mysql-tenant-database-prefix— per-tenant database name prefix (defaulttenant_).--mysql-min-connections/--mysql-max-connections— pool sizing; the pool’s built-in sizing applies when unset.
Nimbus samples MySQL’s global max_allowed_packet when the provider starts.
If --mysql-url also sets the driver’s client-side max_allowed_packet query
parameter, Nimbus uses the lower of the client and server limits. Durable
journal batches are split into ordered statements before either ceiling is
crossed. Restart Nimbus after changing the global packet limit so new provider
connections and the journal planner use the same value.
Run on libSQL / Turso
Section titled “Run on libSQL / Turso”Replica-connected SQLite: Nimbus keeps a local replica cache and connects to a remote libSQL primary. Each tenant gets an isolated namespace:
nimbus start \ --tenant-provider libsql-replica \ --libsql-url "libsql://your-db.turso.io" \ --libsql-auth-token "<token>" \ --libsql-admin-url "https://admin.your-db.example.com" \ --libsql-replica-cache-dir ./cacheThree flags are required: --libsql-url (the primary),
--libsql-admin-url (the admin API Nimbus uses to provision tenant
namespaces), and --libsql-replica-cache-dir (the local replica cache
root). Optional flags:
--libsql-auth-token— auth token for the primary.--libsql-admin-auth-header—Authorizationheader value for the admin API.--libsql-metadata-namespace— metadata namespace (defaultnimbus_provider).--libsql-tenant-namespace-prefix— per-tenant namespace prefix (defaulttenant_).
Run on redb
Section titled “Run on redb”The retained embedded key-value backend. One database file per tenant in
the data directory (<data-dir>/<tenant-id>.redb):
nimbus start --tenant-provider redb --data-dir ./dataPrefer SQLite for new deployments unless you have a specific reason to use redb.
Configure via environment or config file
Section titled “Configure via environment or config file”Every flag above has a NIMBUS_* environment variable and a JSON config-file
key. Precedence is CLI flag, then environment variable, then config file:
export NIMBUS_TENANT_PROVIDER=postgresexport NIMBUS_POSTGRES_URL="postgresql://user:pass@db.example.com:5432/nimbus"nimbus startPass a JSON config file with --config <path> or NIMBUS_CONFIG=<path>.
Storage settings live under the persistence key:
{ "persistence": { "tenant_provider": "postgres", "postgres_url": "postgresql://user:pass@db.example.com:5432/nimbus" }}See the configuration reference for every key.
Note that flags for a backend you have not selected are rejected at startup —
for example, --postgres-url without --tenant-provider postgres is an
error.
Tenant isolation boundaries
Section titled “Tenant isolation boundaries”Tenants never share storage, whichever backend is active:
| Backend | Isolation boundary |
|---|---|
| SQLite | Database file per tenant |
| Postgres | Schema per tenant |
| MySQL | Database per tenant |
| libSQL / Turso | Namespace per tenant |
| redb | Database file per tenant |
For the isolation model itself, see tenant isolation and the concept page.
Control-plane database
Section titled “Control-plane database”Cross-tenant control-plane data is stored in a separate embedded redb
database, nimbus-control.db, inside the control data directory. That
directory defaults to the data directory; override it with
--control-data-dir or NIMBUS_CONTROL_DATA_DIR. The control plane stays
local even when tenant data lives in Postgres, MySQL, or libSQL.
Encrypt local data
Section titled “Encrypt local data”Nimbus can encrypt the local files it owns — embedded SQLite and redb tenant databases, the control-plane database, and libSQL replica caches. With Postgres or MySQL, tenant data lives in the external database and its encryption is the database’s responsibility; Nimbus still encrypts the local control plane. See encryption at rest.