scribase
Concept

Postgres branching, explained with commands you can run

A database branch is a full, writable Postgres that starts as a copy of another one without copying its data first. You can test a migration on real data, give every pull request its own database, and throw the branch away when you are done.

Last checked October 8, 2026.

How copy-on-write branching works

Postgres stores tables as 8 KB pages in files. A copy-on-write branch starts by pointing at the same pages as its parent. Nothing is duplicated until the branch writes: the page it changes is copied, changed, and from then on belongs to the branch alone. The parent never sees it.

That is why a branch of a large database is ready about as fast as an empty one, and why a branch that only adds a column uses almost no extra disk. The cost grows with what the branch changes, not with the size of the parent.

The alternative, pg_dump of the parent into a new database, copies every row up front. It works, but the time and disk grow with the database, so teams end up sharing one stale staging copy instead of branching per change.

ApproachTime to a usable copyExtra diskReal data
pg_dump and restoreGrows with database sizeA full copyYes, as of the dump
Template database (CREATE DATABASE ... TEMPLATE)Grows with size; needs no connections on the templateA full copyYes
Schema-only databaseFastSmallNo rows
Copy-on-write branchIndependent of sizeOnly changed pagesYes, or masked

Create, use and delete a branch

Every hosted branch has its own database, API URL and keys, so an app or a test run points at it like any other environment.

scribase CLI, hosted branchessh
# A writable copy of production with its rows
scribase branch create acme shop add-invoices --from production --data snapshot

# Its URL and keys
scribase branch url acme shop add-invoices

# Start over from the parent's current data (URL and keys stay)
scribase branch reset acme shop add-invoices

# Done
scribase branch delete acme shop add-invoices

Choose what data the branch gets

--dataWhat the branch holdsUse it for
snapshotAn exact copy of the parent rowsReproducing a bug on real data
sanitizedA copy with the columns in your masking rules anonymizedPreviews and coding agents that should not see personal data
schema_onlyTables, functions and policies with no rowsCI runs that seed their own fixtures

Branch from a point in time

Because old page versions are kept for a history window, a branch can start from the database as it was at a past moment. That turns "what did this table look like before the bad deploy" into a query instead of a restore. The default window on Scribase's engine is one day.

sh
scribase branch create acme shop before-deploy --from production \
  --from-time 2026-10-08T09:30:00Z --data snapshot

Give branches an expiry

Forgotten branches are the main cost of branching. A TTL deletes the branch through the same audited delete as a manual one.

sh
scribase branch create acme shop pr-42 --from production --data sanitized --ttl 48h

Diff and merge schema changes

Data stays on the branch; schema changes are what you bring back. Register the branch against its parent to record a merge base, review the diff, then merge. Without --force, a merge stops when both sides changed the same object.

sh
scribase branch register acme shop pr-42 --base production
scribase migrate acme shop pr-42 --dir ./migrations --apply
scribase branch diff  acme shop production pr-42
scribase branch merge acme shop production pr-42
scribase branch pr    acme shop pr-42 github acme/shop 42   # link the PR

Local branches on your laptop

The same idea works locally against the Postgres in your project folder, with no account.

sh
scribase branch new my-feature
psql "$(scribase branch url my-feature)"
scribase branch list
scribase branch delete my-feature

Guardrails

  • Protected branches refuse delete, reset and restore, even from an admin.
  • Reset and delete ask for confirmation on a terminal and need a token with the destructive scope.
  • A coding agent gets the same commands over the MCP server, with the same masking and protection rules.
FAQ

Questions people ask

Is a branch a separate database?

Yes. It is a separate, writable Postgres with its own connection details and API keys. It shares unchanged pages with its parent on disk, which is invisible to queries.

Do writes on a branch affect production?

No. Changed pages are copied to the branch before they are written, so the parent never sees them.

Can I merge data back from a branch?

Merges carry schema changes (tables, columns, functions, policies). Move data back with your own migration or script, so production data changes stay explicit.

What do branches cost on Scribase?

Branches and preview environments are part of a project's flat $25 a month.