Database
In Notarium, notes are files, and the search index and graph are rebuilt from them. But part of the state can't be derived from files: a separate metadata (meta) DB holds it. By default this is a SQLite file under the data root — <DATA_DIR>/meta.db; the META_DB_URL variable is only needed to point to an external Postgres.
What the metadata DB stores
| Data | Why it isn't from files |
|---|---|
| Note identifiers | The notarium-id ↔ path registry: it survives move/rename. |
| Version history | The revision log (version snapshots, where an edit came from) is kept by the app itself, not by git. |
| Users and access | Accounts, roles, membership, tokens. |
| Rename history | Aliases for the old slugs of spaces and projects — so previous addresses keep resolving. |
None of this can be reconstructed from .md files alone — which is why the metadata DB must always be included in your backup. There's a registry of spaces in this DB too, but it's derived: a space's identity lives in a marker file at its root and is recovered by a scan (File-first). The engine's derived indexes (<DATA_DIR>/engine) are simply rebuilt from files on the next start if lost.
For more on versions and where they come from, see Concepts: versioning.
Schema and migrations
The application owns the metadata DB schema: migrations are applied at startup, and there's no separate command for them. The DB itself carries a ledger of the migrations applied to it — that's how a build knows what it's dealing with.
Startup accepts exactly three states:
- an empty DB — the base schema is laid down, and the ledger entry is written in the same transaction;
- a DB whose ledger is an exact prefix of the expected one — versions, names, and checksums are verified, then the missing tail is applied;
- a non-empty DB with no ledger — startup fails closed, before any schema change or application query.
That last one is deliberate. A build doesn't guess the version of a database it doesn't recognize, and it won't stamp the ledger on its own: that would corrupt data silently. If such a database is what you have on hand (an instance older than the base schema, say), first upgrade and check it with the standard procedure for its own version, and only then move it across this boundary.
Notarium's mechanism for rolling data back is a verified archive, not reverse SQL migrations. Take a backup and verify it before you upgrade: Backup and restore.
SQLite (default)
Zero-config: by default the meta DB is sqlite:<DATA_DIR>/meta.db, that is, a file on the /data volume. No separate service is needed, and there's nothing to configure. This is enough for a personal instance and a small team on a single container.
Postgres (for a team and shared state)
To move state outside the container — for shared storage, fault tolerance, or maintenance — point to Postgres:
# .env
META_DB_URL=postgres://user:pass@db:5432/notarium
You need Postgres when state must live independently of the container's lifecycle. The notes themselves still remain files under <DATA_DIR>/spaces — only what can't be derived from them goes into the DB.
password mode relies on the metadata DBIn AUTH_MODE=password mode, the metadata DB is needed for accounts and tokens — and it's already there by default (SQLite under the data root). You don't need to set META_DB_URL separately; you specify it only to move to Postgres. Only AUTH_MODE=none works without a metadata DB. See Authentication.
Moving state into Postgres doesn't by itself enable horizontal scaling: Notarium runs as a single instance, and multiple instances behind a load balancer aren't supported (see Production).