---
title: Dolt Backend for Beads
description: "How beads uses Dolt for versioned issue storage: embedded vs server mode, remotes, sync, and backups"
---
Beads uses Dolt as its storage backend. Dolt provides a version-controlled SQL database with cell-level merge, native branching, and two deployment modes.
## Why Dolt?
- **Native version control** — cell-level diffs and merges, not line-based
- **Multi-writer support** — server mode enables concurrent agents
- **Built-in history** — every write creates a Dolt commit
- **Native branching** — Dolt branches independent of git branches
- **Single-binary option** — embedded mode for solo users (no server needed)
## Getting Started
### Install Dolt (Server Mode Only)
Embedded mode includes everything in the `bd` binary; no separate Dolt install
is needed. Install the standalone `dolt` CLI only when you want to run server
mode or work directly with the database via `dolt sql`.
Install a specific version. Do not install `releases/latest` — see
[Which Dolt version to install](#which-dolt-version-to-install) for the
version to use and why the pin exists.
```bash
DOLT_VERSION=2.5.1 # see "Which Dolt version to install" below
os=$(uname -s | tr '[:upper:]' '[:lower:]')
arch=$(uname -m | sed -e 's/^x86_64$/amd64/' -e 's/^aarch64$/arm64/')
curl -fsSL "https://github.com/dolthub/dolt/releases/download/v${DOLT_VERSION}/dolt-${os}-${arch}.tar.gz" \
| tar -xz -C /tmp
sudo install -m 0755 "/tmp/dolt-${os}-${arch}/bin/dolt" /usr/local/bin/dolt
# Verify you got the version you asked for
dolt version
```
`brew install dolt` installs whatever version the Homebrew formula currently
points at, which is not pinned. If you install that way, check `dolt version`
against the pin below.
### Which Dolt version to install
Beads pins Dolt to **2.5.1**. CI installs that same pin with
`scripts/ci/install-dolt.sh`, which records the per-version measurements and
the criterion for raising it; raise the pin here and in that script together.
This pin is about the standalone `dolt` CLI, which only server and
proxied-server mode use. Embedded mode is unaffected either way: it links the
Dolt 2.5.1 engine into `bd` (`dolthub/driver` v2.4.1 with
`github.com/dolthub/dolt/go` at the v2.5.1 release, in `go.mod`), no matter
which `dolt` CLI is on your PATH. The CLI pin tracks that engine, so every mode
runs the same Dolt. `bd` recommends Dolt **2.5.0** or newer, the first release
whose engine is correct on the query shape in the table below, and managed
proxied-server mode warns below it.
Dolt 2.3.3 is the first release that stores a descending index column as
descending (dolthub/dolt#11746), and older releases cannot read a table that
holds one: even a plain `INSERT` fails with `table has unknown fields`, and an
`UPDATE` or `DELETE` on `issues` fails with `cannot add or update a child row:
a foreign key constraint fails` on `fk_comp_snap_issue`. Migration 0010
declares `compaction_snapshots.idx_comp_snap_issue` with `created_at DESC`. So
every database `bd` creates in embedded mode from this release on, and every
database whose schema a `dolt sql-server` 2.3.3 or newer created, holds one.
**This is one-way: `bd` does not convert the index back**, so such a database
needs Dolt 2.3.3 or newer, and a `bd` built on Dolt 2.3.3 or newer, for good.
Other databases keep an ascending index and stay readable and writable by
older Dolt even after this release migrates and writes to them (measured
2026-10-21 with Dolt 2.2.0 and 2.3.2).
Dolt 2.3.0 (released 2026-08-13) regressed `CALL DOLT_RESET('--hard')`. A few
percent of freshly created databases come up with that procedure unusable —
every call answers `Error 1105 (HY000): context canceled`, from any session
and any new connection, for the life of that server process. Nothing else
about the database looks wrong: `SELECT 1`, `CALL DOLT_CLEAN()`,
`CALL DOLT_CHECKOUT('.')`, `CALL DOLT_COMMIT()` and soft resets all work
normally, so the damage is invisible until something needs a hard reset.
The table records, per release, whether it opens a database `bd` creates,
whether its engine answers an ungrouped `MAX` or `MIN` over an unmatched
primary-key prefix correctly (the shape of dolthub/dolt#12093), and the
`DOLT_RESET('--hard')` regression, measured by creating fresh databases and
immediately calling the procedure:
| Dolt | Opens a database `bd` creates | Ungrouped `MAX`/`MIN` over an unmatched primary-key prefix | Fresh databases with `DOLT_RESET('--hard')` broken |
|---|---|---|---|
| 2.1.8 | no | correct | 0 / 40 |
| 2.2.0 | no | correct | 0 / 60 |
| 2.3.0 | no | correct | 3 / 60 |
| 2.3.1 | no | correct | 3 / 100 (2026-08-20); 8 / 150 (2026-10-10); 4 / 300 (2026-10-21) |
| 2.3.2 | no | correct | not measured (carries the fix, dolthub/dolt#11652) |
| 2.3.3 – 2.3.5 | **yes (the floor)** | correct | not measured |
| 2.4.0 | yes | **no row** (dolthub/dolt#12093) | not measured |
| 2.4.1 | yes | **no row** | 0 / 250 |
| 2.4.2 | yes | **no row** | 0 / 250 |
| **2.5.0** | yes | correct | 0 / 500 |
| **2.5.1** (pinned) | yes | correct | 0 / 500 |
"Opens a database `bd` creates" means `information_schema` reads and writes
don't fail with `table has unknown fields`. Every row of that column is
measured (2026-10-23, on a database `bd` created in embedded mode); plain
reads of `issues` still succeed on the releases marked "no".
- The embedded engine measured 0 / 100 on 2.5.1 and 0 / 100 on 2.4.1
(2026-10-21), and 0 / 60 on 2.4.1 (2026-10-10). The 2026-10-10 and
2026-10-21 runs re-measured 2.3.1 as a positive control, confirming the
method still reproduces the regression.
- The `MAX`/`MIN` column is bee-ghosttrack's reproduction from
[#7646](https://github.com/gastownhall/beads/pull/7646), reproduced on
linux/amd64 and darwin/arm64. `bd`'s one query of that shape, the
versioned-history revision read, no longer depends on it.
- 2.4.0 through 2.4.2 also panic on an ungrouped `MAX` of a non-key column under
a fully bound primary key (dolthub/go-mysql-server#4037).
- "Correct" is for the shape the column names. Every release before 2.5.0,
2.2.0 included, returns no row for an ungrouped `MAX` over the whole primary
key of an empty table (`SELECT COALESCE(MAX(pk), 0) FROM t`); 2.5.0 and
later return one row. `bd`'s production queries of that shape accept either
answer.
- Releases before 2.4.0 drop a join condition when an `ON` clause has an `OR`
over indexed columns (dolthub/dolt#11886).
Raise the pin only once a newer release is confirmed clean by that same
measurement — not because it is newer.
Pin rather than track `latest` for a second, independent reason: the upstream
`releases/latest` URL resolves to the most recently *created* release, not the
highest version, so it can move backwards — v1.88.2 was created on
2026-08-17, after both v2.2.4 and v2.3.0.
#### If you are already running 2.3.x
Whether a database is affected is decided per database, when it is created —
two databases on the same server can differ — so check each one you care
about, against the server that is actually serving it:
```bash
# 1. The working set must be clean first: on a dirty working set a hard
# reset discards uncommitted changes. If this returns any rows, commit
# or clean up before step 3.
dolt sql -q "SELECT * FROM dolt_status"
# 2. Control — this must succeed. If it does not, you have a different
# problem and step 3 proves nothing.
dolt sql -q "SELECT 1"
# 3. On a clean working set this is a no-op on a healthy database.
dolt sql -q "CALL DOLT_RESET('--hard')"
```
Step 2 succeeding while step 3 answers `Error 1105 (HY000): context canceled`
is the discriminating result — that database is affected. Both succeeding
means it is not.
The breakage lives in the running server process, not on disk, so restarting
`dolt sql-server` clears it — verified by re-running the check on an affected
database after a restart, with a healthy database on the same server as the
control. A restart re-rolls the dice for every database, though, so the
durable fix is to move to the pinned version.
This matters because `bd flatten` and the Dolt-history compaction in
`bd admin compact` both finish by hard-resetting `main` onto a temporary
branch, and the merge-settle path behind `bd dolt pull` / `bd sync` falls
back to a hard reset when it has to abandon a merge. See
[Maintenance](#maintenance--bd-prune-and-bd-purge).
### New Project
```bash
# Embedded mode (single writer, no server — default for standalone)
bd init
# Server mode (multi-writer, e.g. orchestrator)
gt dolt start # Start the Dolt server
bd init --server # Initialize with server mode
```
### Migrate from SQLite (Legacy)
If upgrading from an older version that used SQLite:
> **Note:** The `bd migrate --to-dolt` command was removed in v0.58.0.
> For pre-0.50 installations with JSONL data, use the migration script:
>
> ```bash
> scripts/migrate-jsonl-to-dolt.sh
> ```
>
> See [Troubleshooting](/reference/troubleshooting#circuit-breaker-server-appears-down-failing-fast) if you encounter connection errors after migration.
Migration creates backups automatically. Your original SQLite database is preserved as `beads.backup-pre-dolt-*.db`.
## Modes of Operation
```mermaid
flowchart LR
subgraph embedded["Embedded mode (default) — bd init"]
bd1["bd process
Dolt runs in-process"] --> d1[(".beads/embeddeddolt/
single writer, file-locked")]
end
subgraph server["Server mode — bd init --server"]
bd2["bd (agent 1)"] --> srv["dolt sql-server"]
bd3["bd (agent 2)"] --> srv
srv --> d2[(".beads/dolt/
concurrent writers")]
end
```
### Embedded Mode (Solo / Standalone)
In-process Dolt engine — no separate server needed. This is the default for
standalone Beads users. The `bd` binary includes everything; just `bd init` and go.
- Single-writer (one process at a time)
- Data lives in `.beads/embeddeddolt/` alongside your code
- Push to GitHub with `bd dolt push` — code and issues in one repo
- Zero ops: no server, no ports, no PID files
### Server Mode (Multi-Writer / Orchestrator)
Connects to a running `dolt sql-server` for multi-client access.
```bash
# Start the server (orchestrator)
gt dolt start
# Or manually
cd ~/.dolt-data/beads && dolt sql-server --port 3307
```
```bash
# Initialize in server mode
bd init --server
# Or switch via environment variable
export BEADS_DOLT_SERVER_MODE=1
```
```yaml
# .beads/config.yaml (server mode settings)
dolt:
mode: server
host: 127.0.0.1
port: 3307
user: root
```
Configure the connection with flags or environment variables:
| Flag | Env Var | Default |
|------|---------|---------|
| `--server-host` | `BEADS_DOLT_SERVER_HOST` | `127.0.0.1` |
| `--server-port` | `BEADS_DOLT_SERVER_PORT` | `3307` |
| `--server-socket` | `BEADS_DOLT_SERVER_SOCKET` | (none; uses TCP) |
| `--server-user` | `BEADS_DOLT_SERVER_USER` | `root` |
| | `BEADS_DOLT_PASSWORD` | (none) |
**`BEADS_DOLT_SERVER_PORT` also declares the server externally managed.**
Setting it — or the legacy `BEADS_DOLT_PORT` — makes `ResolveServerMode`
classify the workspace as an externally-managed server, so bd will not
auto-start a server for it: if nothing is listening on that port you get
`BEADS_DOLT_SERVER_PORT declares the Dolt server at 127.0.0.1:
externally managed, so bd did not start one` rather than a server started on
it. Use these vars to point bd at a server you already run; they are not a way
to choose the port of a bd-owned server. A
`dolt_mode: proxied-server` workspace is exempt — it reaches its server
through the proxy, so an ambient port does not describe its lifecycle — and
a shared-server workspace is already classified external on its own.
**Unix domain sockets:** Use `--server-socket` to connect via a Unix socket
instead of TCP. This avoids port conflicts between concurrent projects and is
useful in sandboxed environments (e.g., Claude Code) where file-level access
control is simpler than network allowlists. The Dolt server must be started
with `dolt sql-server --socket `. Auto-start is not supported in socket
mode.
Switch to server mode when you need:
- Multiple agents writing simultaneously
- Orchestrator multi-rig setups
- Federation with remote peers
## Maintenance — `bd prune` and `bd purge`
`bd prune` permanently deletes closed non-ephemeral beads to reclaim storage
and shrink auto-exports. `bd purge` does the same for ephemeral beads (wisps,
transient molecules). Both require `--force` to execute.
```bash
bd prune --older-than 30d # Preview closed beads >30d old
bd prune --older-than 30d --force # Delete them
bd prune --older-than 90d --dry-run # Detailed preview with stats
bd purge --force # Delete all closed ephemeral beads
```
**Reference-aware protection:** `bd prune` automatically skips closed beads
whose ID appears in the description, notes, or comments of any open or
in-progress bead. This prevents accidental deletion of ADR, decision, and
verification beads that downstream work still cites. Use
`--ignore-references` to override when cleaning up known-stale references:
```bash
bd prune --older-than 90d --ignore-references --force
```
`bd purge` is unaffected — ephemeral beads' references are themselves
transient. For full Dolt storage reclaim after deleting many rows, follow
with `bd flatten`.
**On Dolt 2.3.x, storage-reclaim operations can fail partway.** `bd flatten`
and the Dolt-history compaction in `bd admin compact` both build a temporary
branch and then hard-reset `main` onto it, and the merge-settle path behind
`bd dolt pull` / `bd sync` falls back to a hard reset when it abandons a
merge. On an affected database that hard reset returns
`Error 1105 (HY000): context canceled`: `bd flatten` and `bd admin compact`
stop at that step, and an abandoned merge is left without its rollback. See
[Which Dolt version to install](#which-dolt-version-to-install) for the check
and the fix.
### Why a garbage collection can reclaim nothing
Dolt's garbage collector is **generational**. Every pass moves the data
reachable at that moment into an *old generation*, and a default pass only
examines the *new generation* — data written since the last collection. So the
second collection on a store never revisits what the first one kept, however
much of it has since become unreachable. Squashing history and then running a
default `dolt gc` is the case where this bites: the orphaned commit chain
usually sits in the old generation, and the pass frees almost nothing.
`dolt gc --full` collects both generations. It frees everything a default pass
frees, plus the old generation, so it is never worth running a default pass
first to save time. If a full collection frees nothing, the remaining bytes are
still referenced — by a branch, a tag, or a cached remote-tracking ref — and
the fix is to remove the reference, not to collect again. The
[History Bloat runbook](/recovery/history-squash) walks through that diagnosis;
[`bd flatten`](/cli-reference/flatten), [`bd compact`](/cli-reference/compact),
and [`bd gc`](/cli-reference/gc) document the collection each one runs.
## Migrating Between Backends
You can migrate data between embedded mode and server mode using `bd backup`.
Both directions preserve full Dolt commit history.
`bd export` is not a substitute for this flow. JSONL exports contain issue
records from the issues table for migration and interoperability; they do not
capture Dolt branches, full commit history, working-set state, or non-issue
tables. Use `bd backup` or a manual Dolt backup when you need a restorable
database backup.
### Server → Embedded
1. **Create a backup from the server-mode project:**
```bash
# In the server-mode project directory
bd backup init /path/to/backup-dir
bd backup sync
```
2. **Create a new embedded-mode project and restore:**
```bash
mkdir new-project && cd new-project
bd init # creates an embedded-mode project by default
bd backup restore --force /path/to/backup-dir
```
`--force` overwrites the freshly-initialized database with the backup
contents. The restore automatically:
- Updates `metadata.json` to match the restored project identity
- Registers the backup directory for future `bd backup sync`
- Backfills the embedded migration tracker (`schema_migrations`)
3. **Verify:**
```bash
bd list
bd backup status
```
### Embedded → Server
1. **Create a backup from the embedded-mode project:**
```bash
# In the embedded-mode project directory
bd backup init /path/to/backup-dir
bd backup sync
```
2. **Create a new server-mode project and restore:**
```bash
mkdir new-project && cd new-project
bd init --server # creates a server-mode project
bd backup restore --force /path/to/backup-dir
```
3. **Verify:**
```bash
bd list
bd backup status
```
### Backup Commands Reference
| Command | Description |
|---------|-------------|
| `bd backup init ` | Register a backup destination (filesystem or DoltHub URL) |
| `bd backup sync` | Push database to the configured backup destination |
| `bd backup restore [path]` | Restore from a backup directory (`--force` to overwrite) |
| `bd backup remove` | Unregister the backup destination |
| `bd backup status` | Show backup configuration and last sync time |
### Notes
- Data locations differ between modes: `.beads/embeddeddolt/` (embedded) vs `.beads/dolt/` (server)
- The backup directory is a full Dolt backup, not an `issues.jsonl` export — it can be on a local drive, NAS, or DoltHub
- You can also migrate via Dolt remotes (`bd dolt push` / `bd dolt pull`) if both projects share a remote
The sections below are the canonical backend migration reference.
## Federation (Peer-to-Peer Sync)
Federation lets independent Dolt-backed workspaces ("towns") sync issues
directly with each other via `bd federation add-peer`/`sync`/`status`,
without a central hub. Credentials are AES-256 encrypted and stored locally.
See [Federation Setup Guide](/multi-agent/federation) for the full setup guide, including
peer configuration, sovereignty tiers, sync/status/topology details, and
troubleshooting.
## Dolt Remotes
Use `bd dolt remote add` to configure remotes. This ensures the running Dolt SQL
server sees the remote immediately. Remotes added directly with the `dolt` CLI
are written to filesystem config and may not be visible to the server until
restart.
```bash
# DoltHub (public or private)
bd dolt remote add origin https://doltremoteapi.dolthub.com/org/beads
# S3
bd dolt remote add origin aws://[bucket]/path/to/repo
# GCS
bd dolt remote add origin gs://[bucket]/path/to/repo
# Git SSH (GitHub, GitLab, etc.)
bd dolt remote add origin git+ssh://git@github.com/org/repo.git
# Local file system
bd dolt remote add origin file:///path/to/remote
```
### Push/Pull
```bash
bd dolt push
bd dolt pull
```
`bd dolt remote add` registers the remote through the Dolt store API. SQL
remotes are the source of truth for `bd dolt remote list`, `bd dolt push`, and
`bd dolt pull`.
For git-protocol remotes, credentialed external-server remotes, and cloud
remotes whose credentials are only present in the current shell, `bd dolt push`
and `bd dolt pull` automatically materialize a matching local CLI remote before
using the `dolt` CLI transport. The CLI remote is a local transport mirror, not
a separate configuration source.
If you are upgrading from an older beads version and previously added remotes
with raw `dolt remote add`, re-register them with `bd dolt remote add
` so they are visible through SQL. `bd doctor` reports legacy CLI-only or
mismatched CLI remotes under `Dolt Remote Migration`.
> **Sharing a Git repo**: Dolt stores data under `refs/dolt/data`, separate
> from standard Git refs (`refs/heads/`, `refs/tags/`). You can safely point a
> `git+ssh://` remote at the same repository as your project source code. See
> [Dolt Git Remotes](https://docs.dolthub.com/concepts/dolt/git/remotes).
### List/Remove Remotes
```bash
bd dolt remote list # Shows SQL-configured remotes
bd dolt remote remove origin # Removes the remote
```
## Contributor Onboarding (Clone Bootstrap)
When someone clones a repository that uses Dolt backend:
1. Run `bd bootstrap` in the clone
2. If the git remote has `refs/dolt/data` (pushed via `bd dolt push`),
`bd bootstrap` auto-detects it and clones the database from the remote
3. Work continues normally — all existing issues are available
**No manual steps required** beyond `bd bootstrap`. The auto-detect:
- Probes `origin` for `refs/dolt/data`
- Clones the Dolt database from the remote (instead of creating a fresh one)
- Configures the Dolt remote for future `bd dolt push`/`pull`
If `sync.remote` is set in `.beads/config.yaml`, that takes precedence
over auto-detection. Any Dolt-compatible remote URL is supported (DoltHub,
S3, GCS, file, or git). On brand-new projects, `bd init` auto-detects
`git origin` and persists it as `sync.remote`, so the first `bd dolt push`
publishes Dolt history to `refs/dolt/data` on the same git remote.
### Verifying Bootstrap Worked
```bash
bd list # Should show issues
bd vc status # Should show the current branch, no uncommitted changes
```
## Troubleshooting
### Server Not Running
**Symptom:** Connection refused errors when using server mode.
```
failed to create database: dial tcp 127.0.0.1:3307: connect: connection refused
```
**Fix:**
```bash
gt dolt start # Orchestrator command
# Or
gt dolt status # Check if running
```
### Bootstrap Not Running
**Symptom:** `bd list` shows nothing on fresh clone.
**Check:**
```bash
ls .beads/dolt/ # Should NOT exist (pre-bootstrap)
BD_DEBUG=1 bd list # See bootstrap output
```
**Force bootstrap:**
```bash
rm -rf .beads/dolt # Remove broken state
bd list # Re-triggers bootstrap
```
### Database Corruption
**Symptom:** Queries fail, inconsistent data.
**Diagnosis:**
```bash
bd doctor # Basic checks
bd doctor --deep # Full validation
bd doctor --server # Server mode checks (if applicable)
```
**Recovery options:**
1. **Repair what's fixable:**
```bash
bd doctor --fix
```
2. **Rebuild from remote:**
```bash
rm -rf .beads/dolt
bd list # Re-triggers bootstrap
```
### Already Committed `.beads/dolt/` to Git
If you accidentally committed a Dolt data directory:
1. Update gitignore: `bd doctor --fix`
2. Remove it from git tracking: `git rm --cached -r .beads/dolt/` (or `.beads/embeddeddolt/`)
3. Commit the removal: `git commit -m "fix: remove accidentally committed dolt data"`
4. To purge from history, use [BFG Repo-Cleaner](https://rtyley.github.io/bfg-repo-cleaner/) or `git filter-repo`
### Lock Contention (Embedded Mode)
**Symptom:** "database is locked" errors.
Embedded mode is single-writer (enforced via file lock). If you need concurrent
access, switch to server mode. See [Migrating Between Backends](#migrating-between-backends).
## Configuration Reference
```yaml
# .beads/config.yaml
# Dolt settings
dolt:
# Auto-commit Dolt history after writes (default: on for embedded, off for server)
auto-commit: on # on | off
# Storage mode (default: embedded)
mode: embedded # embedded | server
# Server mode settings (only used when mode: server)
host: 127.0.0.1
port: 3307
user: root
# Password: env var or credentials file (see below)
# Shared server mode (GH#2377): all projects share a single Dolt server
# at ~/.beads/shared-server/. Each project uses its own database (prefix-based).
# Eliminates port conflicts and reduces resource usage on multi-project machines.
shared-server: false # true | false
```
### Environment Variables
| Variable | Purpose |
|----------|---------|
| `BEADS_DOLT_PASSWORD` | Server mode password (highest priority) |
| `BEADS_CREDENTIALS_FILE` | Path to credentials file (overrides default location) |
| `BEADS_DOLT_SERVER_MODE` | Enable server mode (set to "1") |
| `BEADS_DOLT_SERVER_HOST` | Server host (default: 127.0.0.1) |
| `BEADS_DOLT_SERVER_PORT` | Server port (default: 3307, or 3308 in shared mode) |
| `BEADS_DOLT_SERVER_TLS` | Enable TLS (set to "1" or "true") |
| `BEADS_DOLT_SERVER_USER` | MySQL connection user |
| `BEADS_DOLT_SHARED_SERVER` | Enable shared server mode (set to "1" or "true") |
| `DOLT_REMOTE_USER` | Clone/push/pull auth user |
| `DOLT_REMOTE_PASSWORD` | Clone/push/pull auth password |
| `BD_DOLT_AUTO_COMMIT` | Override auto-commit setting |
**Note:** `BEADS_DOLT_SERVER_PORT` (and the legacy `BEADS_DOLT_PORT`) do more
than name a port — setting either also marks the workspace as an
externally-managed server and suppresses auto-start. See **Server Mode**
above.
### Credentials File
For multi-server setups, you can store passwords in an INI-style credentials file
instead of juggling environment variables per project. Passwords are looked up by
`[host:port]` section, so each project automatically gets the right password based
on its configured server.
**Password resolution order:**
1. `BEADS_DOLT_PASSWORD` env var (highest priority, existing behavior)
2. Credentials file lookup by `[host:port]` (using the resolved runtime port)
3. Empty string (no password)
**Port resolution note:** The `[host:port]` used for credential lookup matches the
resolved runtime port (from the port file, env var, or config — in that priority
order), not necessarily the port stored in `metadata.json`. This matters when using
IAP tunnels: if your tunnel maps remote:3307 to localhost:3308, store your password
under `[127.0.0.1:3308]` and the credentials file will match the actual connection.
**Default location:** `~/.config/beads/credentials` (Linux/macOS), `%APPDATA%\beads\credentials` (Windows)
**Override location:** Set `BEADS_CREDENTIALS_FILE` env var.
**File format:**
```ini
# ~/.config/beads/credentials
[127.0.0.1:3307]
password=localDevPassword
[beads.company.com:3307]
password=teamServerPassword
[10.0.1.50:3308]
password=officePassword
```
**Permissions:** On Linux/macOS, a warning is printed to stderr if the file is
readable by group or others (mirrors ssh behavior). Set permissions with:
```bash
chmod 600 ~/.config/beads/credentials
```
## Dolt Version Control
Dolt maintains its own version history, separate from Git:
```bash
# View an issue's version history across Dolt commits
bd history bd-42
# Show current branch and uncommitted changes
bd vc status
# Create manual checkpoint
bd vc commit -m "Checkpoint before refactor"
```
### Auto-Commit Behavior
In **embedded mode** (standalone default), each `bd` write command creates a Dolt commit:
```bash
bd create "New issue" # Creates issue + Dolt commit
```
In **server mode** (orchestrator), auto-commit defaults to OFF because the server
manages its own transaction lifecycle. Firing `DOLT_COMMIT` after every write
under concurrent load causes 'database is read only' errors.
Override for batch operations (embedded) or explicit commits (server):
```bash
bd --dolt-auto-commit off create "Issue 1"
bd --dolt-auto-commit off create "Issue 2"
bd vc commit -m "Batch: created issues"
```
## Server Management (Orchestrator)
The orchestrator provides integrated Dolt server management:
```bash
gt dolt start # Start server (background)
gt dolt stop # Stop server
gt dolt status # Show server status
gt dolt logs # View server logs
gt dolt sql # Open SQL shell
```
Server runs on port 3307 (avoids MySQL conflict on 3306).
### Standalone-to-managed-city handoff
When an existing standalone project is later added to a managed city or
orchestrator, avoid letting two Dolt servers become sources of truth for the
same beads database name. A common split-brain symptom is that `.beads/dolt-server.port`
points at the old standalone server while the shell environment points `bd` at
the managed server with `BEADS_DOLT_PORT` or `BEADS_DOLT_SERVER_PORT`.
Check before migrating:
```bash
bd doctor
bd dolt status
```
`bd doctor` warns when the runtime managed port differs from the local port
file. The warning is intentionally diagnostic only; do not delete the local port
file until the standalone store has been exported and imported into the managed
server.
Safe manual handoff:
```bash
# From the standalone project, without managed-city port overrides:
unset BEADS_DOLT_PORT BEADS_DOLT_SERVER_PORT
bd backup
bd export > /tmp/beads-standalone.jsonl
bd dolt stop
# Then enter the managed-city environment and import into its Dolt server:
bd import /tmp/beads-standalone.jsonl
bd doctor
```
After `bd doctor` shows one healthy store and the imported issue count is
correct, archive the old local Dolt data directory instead of deleting it
immediately. Keep the backup until the managed city has been pushed or otherwise
snapshotted.
### Shared Server Mode
On machines with multiple beads projects, each project normally starts its own Dolt server.
Shared server mode runs a single Dolt server at `~/.beads/shared-server/` that serves all projects:
```bash
# Enable for this project (config.yaml key)
bd config set dolt.shared-server true
# Or enable machine-wide via environment variable
export BEADS_DOLT_SHARED_SERVER=1
# Or enable during init
bd init --prefix myproject --shared-server
```
**Benefits:**
- No port conflicts between projects (single server on port 3308, avoids orchestrator on 3307)
- Reduced resource usage (one process instead of many)
- Automatic database isolation (each project uses its own database name)
**How it works:**
- Server state files (PID, port, lock, log) live in `~/.beads/shared-server/`
- Dolt data directory: `~/.beads/shared-server/dolt/`
- Each project's database is stored as a subdirectory (e.g., `~/.beads/shared-server/dolt/myproject/`)
- The file lock mechanism ensures safe concurrent access from multiple projects
- Default port is 3308 (not 3307) to avoid conflict with the orchestrator. Override with `BEADS_DOLT_SERVER_PORT` or `dolt.port` in config.yaml
**Important:** Each project on a shared server **must have a unique prefix** (database name).
Two projects with the same prefix share the same database — if this happens accidentally,
the project identity check will detect the mismatch and refuse to connect, preventing
silent data corruption. Always use distinct prefixes when running `bd init --shared-server`.
```bash
# Check shared server status from any project
bd dolt status
# Show full configuration including shared mode
bd dolt show
```
### Data Location (Orchestrator)
```
/.dolt-data/
├── hq/ # Town beads (hq-*)
├── my-project/ # Project rig (mp-*)
├── beads/ # Beads rig (bd-*)
└── other-project/ # Other rig (op-*)
```
### Central Dolt Server (macOS LaunchAgent)
If you do not use the orchestrator but still want a single persistent Dolt
server for multiple projects on macOS, run a custom `LaunchAgent` instead of
spawning per-project embedded instances.
#### Why Not `brew services start dolt`?
After installing Dolt with `brew install dolt`, the natural next step is
`brew services start dolt`. However, the Homebrew formula runs
`dolt sql-server` without the `--config` flag, and Dolt does not auto-discover
`config.yaml` from its working directory. The config file must be passed
explicitly with `--config `.
#### Setup with a Custom LaunchAgent
Install Dolt and initialize its data directory. Check the installed version
against [the pin](#which-dolt-version-to-install) — Homebrew's formula is not
pinned:
```bash
brew install dolt
dolt version
cd /opt/homebrew/var/dolt && dolt init
```
Configure Dolt for port 3307:
```yaml
# /opt/homebrew/var/dolt/config.yaml
log_level: info
listener:
host: 127.0.0.1
port: 3307
max_connections: 100
behavior:
autocommit: true
```
Create the LaunchAgent plist:
```bash
cat > ~/Library/LaunchAgents/com.local.dolt-server.plist << 'EOF'
Label
com.local.dolt-server
ProgramArguments
/opt/homebrew/bin/dolt
sql-server
--config
/opt/homebrew/var/dolt/config.yaml
WorkingDirectory
/opt/homebrew/var/dolt
RunAtLoad
KeepAlive
StandardOutPath
/opt/homebrew/var/log/dolt.log
StandardErrorPath
/opt/homebrew/var/log/dolt-error.log
EOF
```
Load and verify the service:
```bash
launchctl load ~/Library/LaunchAgents/com.local.dolt-server.plist
mysql -h 127.0.0.1 -P 3307 -u root -e "SELECT 1"
```
Point beads at the central server:
```bash
export BEADS_DOLT_SERVER_MODE=1
export BEADS_DOLT_SERVER_PORT=3307
```
Manage the service:
```bash
# Stop
launchctl unload ~/Library/LaunchAgents/com.local.dolt-server.plist
# Restart
launchctl unload ~/Library/LaunchAgents/com.local.dolt-server.plist
launchctl load ~/Library/LaunchAgents/com.local.dolt-server.plist
# Check logs
tail -f /opt/homebrew/var/log/dolt.log
```
## Advanced Dolt Usage
The `dolt` CLI lets you operate directly on the database for power-user
workflows. The data directory depends on your mode: `.beads/embeddeddolt/`
(embedded) or `.beads/dolt/` (server).
### Branching
```bash
cd .beads/dolt # or .beads/embeddeddolt for embedded mode
dolt branch feature-x
dolt checkout feature-x
```
### Time Travel
```bash
dolt log
dolt checkout
dolt sql -q "SELECT * FROM issues"
```
### Diff and Blame
```bash
dolt diff main feature-x
dolt blame issues
```
## Migration Cleanup
After successful migration from SQLite, you may have backup files:
```
.beads/beads.backup-pre-dolt-20260122-213600.db
.beads/sqlite.backup-pre-dolt-20260123-192812.db
```
These are safe to delete once you've verified Dolt is working:
```bash
# Verify Dolt works
bd list
bd doctor
# Then clean up (after appropriate waiting period)
rm .beads/*.backup-*.db
```
**Recommendation:** Keep backups for at least a week before deleting.
## See Also
- [Sync Concepts](/core-concepts/sync-concepts) - The conceptual model behind cross-machine sync (Dolt source of truth, wire format, anti-patterns)
- [Sync Setup Guide](/getting-started/sync-setup) - Setting up sync across multiple computers
- [Federation Setup Guide](/multi-agent/federation) - Peer-to-peer federation setup
- [Configuration](/reference/configuration) - Full configuration reference
- [Dependencies and Gates](/core-concepts/dependencies) - Dependencies and gates
- [Git Integration](/reference/git-integration) - Git worktrees and protected branches
- [Troubleshooting](/reference/troubleshooting) - General troubleshooting