Skip to content

Database Branching

Database branching allows you to create instant, isolated copies of your entire database (both schema and data) to safely perform development, migration tests, and QA procedures.

By leveraging native PostgreSQL templates, Rebase provisions clone databases at the filesystem level. This means you get a full-fidelity replica containing all tables, indexes, custom types, constraints, and Row-Level Security (RLS) policies without any network transfer overhead or schema setup delays.

┌────────────────────────┐
│ Production DB (rebase) │
└───────────┬────────────┘
(CREATE DATABASE ... TEMPLATE)
┌─────────────────┴─────────────────┐
▼ ▼
┌───────────────────────┐ ┌───────────────────────┐
│ rb_feature_auth (Dev) │ │ rb_staging (Staging) │
└───────────────────────┘ └───────────────────────┘

When a database branch is created, the Rebase BranchService executes the following SQL:

CREATE DATABASE "rb_feature_auth" TEMPLATE "rebase";

PostgreSQL processes this operation by copying the underlying filesystem directories containing the source database files. This provides:

  • Sub-Second Clones: No SQL generation or data loading is performed.
  • Identical Schemas and Data: Every row, index, and constraint is duplicated instantly.
  • Complete Isolation: Altering the schema or inserting records into the branch has no impact on the source database.

PostgreSQL requires that no other active connections exist on the template (source) database when running a CREATE DATABASE ... TEMPLATE command.

To prevent failures, the Rebase DatabasePoolManager executes an active eviction process before cloning or dropping a branch:

  1. Eviction Loop: It automatically closes and disconnects all idle pools pointing to the targeted database within the Rebase application context.
  2. External Connections Block: If external clients (such as DBeaver, pgAdmin, or external backend processes) maintain active transactions on the source database, PostgreSQL will reject the template operation with a "being accessed by other users" error. In this scenario, those connections must be closed manually.

Branch configurations are stored in the default database under the rebase.branches table, which is provisioned during bootstrapping:

CREATE SCHEMA IF NOT EXISTS rebase;
CREATE TABLE IF NOT EXISTS rebase.branches (
name TEXT PRIMARY KEY, -- Sanitize user branch name (alphanumeric & underscores)
db_name TEXT NOT NULL UNIQUE, -- Actual PostgreSQL database name (prefixed with 'rb_')
parent_db TEXT NOT NULL, -- Source database cloned from
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
metadata JSONB DEFAULT '{}'
);

The branching API is exposed via the backend’s BranchService. Below is a reference of the core interface:

Generates a new branch database from the default database or an explicit source template.

import { initializeRebaseBackend } from "@rebasepro/server";
const backend = await initializeRebaseBackend({ /* ... */ });
const admin = backend.driver.admin;
// Create a branch from the default database
const newBranch = await admin.createBranch("feature_oauth");
// Create a branch from a specific staging database
const stagingBranch = await admin.createBranch("pr_review_42", {
source: "rb_staging"
});

Retrieves list of registered branches along with their physical sizes queried via the PostgreSQL pg_database_size system function.

const branches = await admin.listBranches();
/*
Output:
[
{
name: "feature_oauth",
parentDatabase: "rebase",
createdAt: 2026-06-20T22:00:00.000Z,
sizeBytes: 83886080 // 80 MB
}
]
*/

Fetches metadata for a single branch. If the branch exists, the service attempts to query its current physical disk usage:

const info = await admin.getBranchInfo("feature_oauth");

Drops the target database from the server and cleans up its record in the rebase.branches metadata table.

await admin.deleteBranch("feature_oauth");

[!CAUTION] Safety Guard: The main database (default database name configured in connection strings) is protected. If you attempt to delete the parent database, the BranchService throws a "Cannot delete the main database" error and aborts.


Database branches can be managed directly using the Rebase CLI.

# Create a new branch named 'dev_sandbox'
rebase db branch create dev_sandbox
# List all branches and disk utilization
rebase db branch list
# Delete a branch
rebase db branch delete dev_sandbox

When you create or switch to a branch, the CLI updates your local development configuration. The DatabasePoolManager dynamically instantiates a new connection pool for the chosen branch database name (e.g. rb_dev_sandbox), letting you test migrations or seed data without manual connection string edits.


Because PostgreSQL duplicates the files on disk, each branch consumes space equal to the source database. If you have a 100GB production database, creating 5 branches will consume an additional 500GB of storage.

  • Recommendation: Use subsetted databases or thin dev-templates as your clone sources instead of full production clones.

When deploying behind pgBouncer or connection poolers, ensure the pooler supports administrative database operations. Creating and dropping databases bypasses standard transaction-level pools and requires direct connections to the Postgres server (using elevated user privileges) via the adminConnectionString configuration.

Because branches are separate PostgreSQL databases, you cannot perform SQL JOIN statements across branch boundaries. All relations must be contained within the scope of the single active branch database.