Skip to content

Forking

A fork is a brand-new database, in the same namespace, populated with a snapshot of the source’s schema and data. Once forked, the two databases are independent — writes to one don’t affect the other.

In the console: Database → Fork in the page header. Pick a name and slug for the fork; PerSQL provisions the new DO and applies the dump.

Forked databases remember where they came from. The detail page shows a “Forked from <source-slug> · 3d ago” breadcrumb that links back to the parent. The lineage stays even if the parent is later deleted (the link goes dead, but the label remains).

The same fields are exposed on the API:

db.forkedFrom; // { id, slug, name } | null
db.forkedAt; // ISO string | null

Only GET /api/namespaces/:ns/databases/:slug resolves the parent’s slug+name; the namespace list endpoint returns just the raw forkedAt to keep the response cheap.

  • Tables, indexes, triggers, views (everything in sqlite_master).
  • All rows of all tables.
  • Saved queries (per-database, scoped to source).
  • Migrations history (per-database).
  • Schedules (per-database).
  • Custom hostnames.
  • API tokens (those are per-namespace and apply to the fork too).
  1. PerSQL streams a SQL dump from the source database via the same path as Export SQL.
  2. A new database row is inserted into the namespace registry with status provisioning.
  3. A fresh DO is initialized at the new database’s id and the dump is applied via importSql (one transaction).
  4. On success, the new database is flipped to healthy.
  5. On failure, the new database row + DO are cleaned up and the error is surfaced to the user.

Forks can carry a self-destruct timer. Pass ttlDays (1–30) on the REST call, or --ttl 7d on the CLI, and the daily 04:00 UTC cron deletes the fork — DO and registry row — once the timestamp passes. Pre-existing forks without a TTL are unaffected.

persql db fork acme/orders pr-142 --ttl 7d

The detail page shows an “Auto-deletes in 6d” line; the database list shows an “expires in 6d” badge that turns red under 6 hours.

For PR-style ephemeral databases, the Branches API is usually a better fit: it’s idempotent (PUT-by-ref creates or resets), so CI doesn’t need to track whether the database already exists.

POST /api/namespaces/:ns/databases/:db/fork
{ "name": "Orders staging", "slug": "orders-staging", "region": "auto", "ttlDays": 7 }

ttlDays is optional. Manage permission required on the source. The response is the new database row, including its expiresAt field.

  • Fork is bounded by the same 50 MB import cap as Import SQL.
  • Forking a hot database briefly pauses writes on the source while we read; in practice this takes hundreds of milliseconds.
  • Cross-namespace fork is not supported in v1 — fork within the namespace, then transfer if needed.