Skip to content
Discord Get Started

Branching Workflows

DB9 can create a branch of any database — an independent copy with its own credentials, connection string, and data. Branches are useful for preview environments, isolated testing, safe schema migrations, and data recovery.

This guide shows how to create, use, and manage branches through the CLI and SDK.

  • A DB9 account with the CLI installed (see Quick Start)
  • An existing database to branch from
Terminal
db9 create --name production

When you create a branch, DB9 copies the full schema and data from the source database into a new, independent database. The branch gets its own:

  • TiKV keyspace (isolated storage)
  • Admin credentials
  • Connection string
  • Lifecycle (create, delete independently of the parent)

Changes to the branch do not affect the parent, and changes to the parent do not propagate to the branch.

Branch creation is asynchronous — the command returns immediately with status CLONING, and you poll until the branch reaches ACTIVE.

Terminal
db9 branch create production --name feature-auth

The command returns immediately with the branch details:

Output
Name: feature-auth
State: CLONING
Parent: production

Poll the branch status until it reaches ACTIVE:

Terminal
db9 db status feature-auth

Or in a script:

Terminal
while true; do
state=$(db9 db status feature-auth --json | jq -r .state)
if [ "$state" = "ACTIVE" ]; then
echo "Branch is ready"
break
elif [ "$state" = "CREATE_FAILED" ]; then
echo "Branch creation failed"
db9 db status feature-auth --json | jq .state_reason
exit 1
fi
sleep 2
done
Terminal
db9 branch create production --name staging --show-secrets

This prints the admin password and connection string in the output. Avoid this in CI logs — use db9 db connect-token instead for short-lived credentials.

A branch is a regular DB9 database. Connect with any PostgreSQL tool:

Terminal
db9 db connect feature-auth

Or get the connection string:

Terminal
db9 db status feature-auth --json | jq -r .connection_string

Then use it with psql, your ORM, or any PostgreSQL driver.

Terminal
db9 branch list production

Shows all branches of a database with their state and creation time. Supports --output json and --output csv for programmatic use.

Terminal
db9 branch delete feature-auth

Deletion is permanent — there is no undo or trash. Deleting a branch does not affect the parent database.

Use the TypeScript SDK for programmatic branch management:

TypeScript
import { createDb9Client } from 'get-db9';
const client = createDb9Client();
// Create a branch (first argument is the parent database ID)
const branch = await client.databases.branch('production-db-id', {
name: 'feature-auth'
});
console.log(branch.id, branch.state); // "CLONING"
// Poll until ready
let status;
do {
status = await client.databases.get(branch.id);
if (status.state === 'CREATE_FAILED') throw new Error(status.state_reason);
await new Promise(r => setTimeout(r, 2000));
} while (status.state !== 'ACTIVE');
// Use the branch — it has its own connection string
console.log(status.connection_string);
// Delete when done
await client.databases.delete(branch.id);

Create a branch per pull request for integration testing:

Terminal
BRANCH_NAME="pr-${PR_NUMBER}"
# Create branch from production
db9 branch create production --name "$BRANCH_NAME"
# Wait for it
while [ "$(db9 db status "$BRANCH_NAME" --json | jq -r .state)" != "ACTIVE" ]; do
sleep 2
done
# Run migrations and tests against the branch
db9 db sql "$BRANCH_NAME" -f migrations/latest.sql
npm test -- --database-url="$(db9 db status "$BRANCH_NAME" --json | jq -r .connection_string)"
# Clean up
db9 branch delete "$BRANCH_NAME"

Test a migration against a real copy of production data before applying it:

Terminal
# Branch production
db9 branch create production --name migration-test
# Wait for ready
# ...
# Apply migration
db9 db sql migration-test -f migrations/0042_add_index.sql
# Validate
db9 db sql migration-test -q "SELECT count(*) FROM users"
db9 db sql migration-test -q "EXPLAIN SELECT * FROM users WHERE email = 'test@example.com'"
# If satisfied, apply to production
db9 db sql production -f migrations/0042_add_index.sql
# Clean up test branch
db9 branch delete migration-test

Give each agent task its own database branch so parallel agents don’t interfere with each other:

Terminal
TASK_ID="task-$(uuidgen | head -c 8)"
db9 branch create shared-db --name "$TASK_ID"
# ... wait for ACTIVE ...
# Agent works against isolated branch
db9 db sql "$TASK_ID" -q "CREATE TABLE scratch (id SERIAL, data JSONB)"
db9 db sql "$TASK_ID" -q "INSERT INTO scratch (data) VALUES ('{\"result\": \"done\"}')"
# Clean up when task completes
db9 branch delete "$TASK_ID"
  • Asynchronous creation — branches are not immediately ready. Always poll for ACTIVE state before connecting.
  • Full copy, not copy-on-write — each branch is an independent database with its own storage. Large databases take longer to branch.
  • Max 2 concurrent branch creations — attempting a third returns a 429 error. Wait for in-progress branches to finish.
  • Branches count toward database quota — anonymous accounts are limited to 5 databases total (including branches). Run db9 claim to remove the limit.
  • No automatic merge — there is no built-in way to merge branch changes back to the parent. Export and re-apply manually.
  • Deletion is permanent — deleted branches cannot be recovered.
  • Branch naming — names must be unique within your account and cannot be empty.