Skip to content

Branches

A branch is a fork of a database that carries a stable external identifier (a ref — typically a PR number or a git branch name). The (parent, ref) pair is unique, so a CI pipeline can PUT the same ref repeatedly and either create the branch the first time or reset it from the parent’s current state every other time.

For each pull request:

  • First commit: PUT /branches/pr-42 → branch is forked from parent.
  • Every later commit: PUT /branches/pr-42 → branch is reset back to the parent’s current state.
  • PR closes: DELETE /branches/pr-42 → branch is gone.

No “is this the first run?” branching in your workflow, no collisions if two jobs race, no manual cleanup of stale forks.

Each database has a Branches tab. Click New branch, give it a ref, and PerSQL forks the parent for you. Each row shows the ref, status, fork timestamp, and an optional auto-delete date. Reset and delete actions live next to each branch.

Terminal window
curl -X PUT \
--cookie cookies.txt \
-H 'Content-Type: application/json' \
-d '{"ttlDays": 7}' \
https://api.persql.com/api/namespaces/acme/databases/main/branches/pr-42

Body fields:

  • name (optional) — display name. Defaults to <parent name> (<ref>).
  • region (optional) — DO location hint, only honored on first create. Defaults to auto.
  • ttlDays (optional, 1–30) — branch is auto-deleted after this many days by the daily 04:00 UTC cron. Reset on every PUT.

Returns the branch’s full database row, including branchRef, forkedFrom, and expiresAt.

Terminal window
curl --cookie cookies.txt \
https://api.persql.com/api/namespaces/acme/databases/main/branches
Terminal window
curl -X DELETE --cookie cookies.txt \
https://api.persql.com/api/namespaces/acme/databases/main/branches/pr-42

The DO is destroyed and the registry row removed.

Refs must be 1–64 characters, start with an alphanumeric, and contain only A-Z, a-z, 0-9, ., _, -, and /. Examples:

  • pr-42
  • feat/login-redesign
  • release-2026.04

The branch’s slug is derived from the parent’s slug + a sanitized ref (e.g. main + pr-42 → slug main-pr-42). Slugs collide gracefully with a short suffix.

Same as a regular fork:

  • Tables, indexes, triggers, views.
  • All rows of all tables.

We don’t copy:

  • Saved queries, migrations history, schedules — those live on the parent and stay there.
  • Custom hostnames.

API tokens are namespace-scoped and apply to the branch automatically.

There is no per-branch fee and no plan-tier gate. Branches bill against the same four meters as the parent:

  • Requests / rows read / rows written — only when the branch is queried. An idle branch costs nothing on these meters.
  • Storage — metered from creation, because a branch is a full copy of the parent’s data (tables, indexes, rows). A 100 MB parent branched for a 7-day PR-preview costs ~$0.01 in storage; the ttlDays auto-delete keeps that bill bounded.

The cheapest branches are branches of small or empty parents. For schema-only experiments against a large parent, fork and DROP the heavy tables before populating fixtures.