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.
| Approach | Time to a usable copy | Extra disk | Real data |
|---|---|---|---|
| pg_dump and restore | Grows with database size | A full copy | Yes, as of the dump |
| Template database (CREATE DATABASE ... TEMPLATE) | Grows with size; needs no connections on the template | A full copy | Yes |
| Schema-only database | Fast | Small | No rows |
| Copy-on-write branch | Independent of size | Only changed pages | Yes, 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.
# 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-invoicesChoose what data the branch gets
| --data | What the branch holds | Use it for |
|---|---|---|
| snapshot | An exact copy of the parent rows | Reproducing a bug on real data |
| sanitized | A copy with the columns in your masking rules anonymized | Previews and coding agents that should not see personal data |
| schema_only | Tables, functions and policies with no rows | CI 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.
scribase branch create acme shop before-deploy --from production \
--from-time 2026-10-08T09:30:00Z --data snapshotGive 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.
scribase branch create acme shop pr-42 --from production --data sanitized --ttl 48hDiff 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.
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 PRLocal branches on your laptop
The same idea works locally against the Postgres in your project folder, with no account.
scribase branch new my-feature
psql "$(scribase branch url my-feature)"
scribase branch list
scribase branch delete my-featureGuardrails
- 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.
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.