What you end up with
bd sync on both ends. Nothing arbitrates between them but the bucket.
Prerequisites
- The Dolt backend (the only supported backend).
- A bucket you can write to, and credentials on both machines:
- GCS: Application Default Credentials —
gcloud auth application-default login(or a service account viaGOOGLE_APPLICATION_CREDENTIALS). - S3: the standard AWS chain —
AWS_PROFILE,AWS_ACCESS_KEY_ID/AWS_SECRET_ACCESS_KEY, or an instance role.
- GCS: Application Default Credentials —
- A Dolt commit identity on every machine — see Failure modes; a fresh machine usually has none, and the first merge is what discovers it.
1. Create the bucket path
One bucket path per beads database. Two databases sharing a path will fight over the same Dolt history.2. Machine one: register the remote and seed it
Run this in the workspace that already holds the database you want to share:gs://, s3:// (or Dolt’s aws://), az://,
dolthub://, https://, file://, and git SSH.
Two details worth knowing:
- Use
bd dolt remote add, not rawdolt remote add. bd registers the remote through the store API so a running Dolt SQL server sees it immediately. A remote added with thedoltCLI lands in filesystem config only, and push/pull then fail with remote not found until the server restarts. - Naming it
originalso persistssync.remoteinto.beads/config.yaml, which is what letsbd syncfind the remote with no flags. Any other name works, but every sync then needsbd sync --remote <name>.
3. Machine two: birth the replica from the bucket
bd init --remote clones the Dolt database from the bucket and persists
sync.remote, so machine two is ready to sync immediately. This is a clone,
not an import: no JSONL round-trip, no re-keying, and the full commit history
comes with it.
Verify the birth before you trust it:
Already have a
.beads/ directory on machine two that you want to replace with
the bucket’s copy? Don’t clone over it. Move it aside first, then run
bd init --remote; see Init Safety for the guard
rails.4. Pick a sync cadence
bd sync is the whole loop — pull, positively check for conflicts, repair
is_blocked, push with bounded retry:
Three rules for choosing the interval:
- Staleness is the interval. Every replica’s view of the others is up to one full interval old, by construction.
- Lease TTL and reclaim grace must both exceed the interval. A lease is
only meaningful on the replica that granted it, and a reaper on the other
machine would be judging liveness from data older than the lease. See
Leases are per-replica,
and name each replica with
bd config set node_id <name>so the cross-replica reclaim guard arms. - A longer interval means more conflicts, not just staler data.
updated_atis touched by every bd mutation, so two replicas editing the same issue between syncs conflict even when the fields they changed are disjoint. Disjoint edits to different issues merge cleanly at any cadence.
Measured cost
From a production two-machine deployment (laptop + Mac Mini, ~1.3k issues, ~115k chunks, Dolt 2.1.10, GCS remote):
A 60-second cadence therefore spends a few seconds per tick, and the steady
state is dominated by no-ops.
Failure modes
bd dolt push says the remote does not exist
The remote was added with raw dolt remote add, so it exists in filesystem
config but not in the SQL server’s dolt_remotes table. Re-register it:
Pull fails on a fresh machine with no conflicts reported
A machine with no Dolt commit identity cannot author the merge commit a pull creates, so even a no-op pull fails. It is the most common first-sync failure on a newly provisioned box:Auth works in the shell but sync fails in server mode
With an external Dolt SQL server,CALL DOLT_PUSH/PULL runs inside the
server process, which only has the environment it inherited at startup.
Credentials exported afterwards never reach it. bd detects cloud credentials
matching the remote’s scheme (GOOGLE_*/GCS_* for gs://, AWS_* for
s3:///aws://, AZURE_STORAGE_* for az://) and routes push/pull through
a dolt CLI subprocess, which inherits the current environment. If sync still
cannot authenticate, restart the server with the credentials in its
environment.
The remote is named something other than origin
A replica born with bd init --remote (or dolt clone) names its remote
origin; a machine where someone added the remote by hand may have named it
anything. Timers must pass the right --remote on each machine — or rename
the remote so both ends match.
A sync exits 2 (conflict)
bd sync halts before recomputing or pushing and never auto-resolves what it
cannot settle safely. Repeated runs keep halting the same way until an
operator resolves the divergence. Inside .beads/dolt/<db>:
One bucket path, two databases
A bucket path is a single Dolt history. Pointing a second, unrelated beads database at the same path produces a divergence no merge can reconcile. Give each database its own path.Relationship to bd federation
Both paths write Dolt remotes; they differ in what they are for.
Registering the bucket as a named peer instead is one command, and the peer
surface adds sovereignty tiers and topologies:
Reference
- Federation Setup — peers, sovereignty, topologies
- Dolt Architecture — remotes, push/pull, storage layout
bd dolt·bd init·bd sync --helpfor the full sync surface- Sync Failures — recovering a wedged sync