Forking
A fork is a brand-new database, in the same workspace, that copies the source’s schema — tables, indexes, triggers, and views — into empty tables. Rows are not copied: the fork starts empty. Once forked, the two databases are fully independent — writes to one don’t affect the other. Seed the fork with fixtures or a migration.
In the console: Database → Fork in the page header. Pick a name and slug for the fork; PerSQL provisions the new database and replays the source’s schema into it.
Lineage
Section titled “Lineage”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 } | nulldb.forkedAt; // ISO string | nullOnly GET /api/namespaces/:ns/databases/:slug resolves the parent’s
slug+name; the workspace list endpoint returns just the raw
forkedAt to keep the response cheap.
What we copy
Section titled “What we copy”- Tables, indexes, triggers, and views — the full schema (every DDL
object in
sqlite_master).
Rows are not copied. The fork starts with the same structure and empty tables; seed it with fixtures or a migration.
What we don’t copy
Section titled “What we don’t copy”- Saved queries (per-database, scoped to source).
- Migrations history (per-database).
- Schedules (per-database).
- Custom hostnames.
- API tokens (those are per-workspace and apply to the fork too).
How it works
Section titled “How it works”- PerSQL reads the source’s schema — the
CREATE TABLE/INDEX/TRIGGER/VIEWstatements — not its rows. - A new database row is inserted into the workspace registry with
status
provisioning. - A fresh database is initialized at the new id and the schema is replayed into it (one transaction).
- On success, the new database is flipped to
healthy. - On failure, the new database row is cleaned up and the error is surfaced to the user.
Ephemeral forks (TTL)
Section titled “Ephemeral forks (TTL)”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 — the database and its registry row — once the timestamp passes.
Pre-existing forks without a TTL are unaffected.
persql db fork acme/orders pr-142 --ttl 7dThe 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.
Caveats
Section titled “Caveats”- A fork copies schema only, so its cost is bounded by the number of schema objects, not the parent’s data size — forking a large database is as cheap and fast as forking an empty one.
- The fork starts with empty tables. If you need the parent’s data,
load it explicitly (fixtures, a migration, or
Import SQL). - Cross-workspace fork is not supported in v1 — fork within the workspace, then transfer if needed.