Docs
Skip to content

PostgreSQL

Branches_

Spin up an ephemeral, isolated copy of your PostgreSQL database in seconds from a storage snapshot. Use branches for previews, migrations, and testing.

3 min read

Raw

A branch is a short-lived, isolated copy of your database. It has its own endpoint and reuses the parent's credentials, because it is a snapshot copy of the parent's storage volume taken at a point in time. Branches are not replicas: once created, they diverge from the parent and never sync back.

Use cases:

  • Preview environments: one branch per pull request, destroyed when the PR closes
  • Test migrations: apply a destructive ALTER against the branch first, observe the behavior, then run it against the source
  • Reproduce a bug: branch the database, attach a debugger, throw the branch away when done
  • Heavy analytical queries: EXPLAIN ANALYZE experiments against a branch cannot slow down the primary

How it works

Creating a branch takes a fast CHECKPOINT on the parent, snapshots the parent's storage volume with a near-instant copy-on-write operation, and provisions a branch instance from the snapshot on the same engine version with its own isolated storage. Branch compute is fixed and lightweight, enough to validate a change rather than carry production load, and is not configurable. The parent and the branch share storage at the moment of branching; storage cost grows as the two diverge.

Create a branch

Both fields are optional:

FieldDefaultPurpose
branchIdauto-generatedCustom ID (a-z, A-Z, 0-9, ., -, _, max 36 chars)
ttl86400 (24 hours)Lifetime in seconds before the branch expires (min 300, max 604800)

The call is asynchronous and returns immediately while the branch provisions in the background. When the TTL elapses, the branch and its storage are removed automatically.

List branches and connect

Each entry carries its metadata and connection details, so there is no separate credentials call:

A branch gets its own hostname, and reuses the parent's username and password because it is a snapshot copy of the parent's storage. The port is the standard 5432; branches have no connection pooler. Connect with the branch's connectionString straight from the response:

Bash
psql "<branch connectionString>"

Delete a branch

Deleting a branch removes the branch's instance, its storage volume, and the underlying snapshot. There is no soft delete: once the branch is gone, the data is gone.

Billing

A branch runs on fixed, lightweight compute, so its cost is dominated by storage. The snapshot is free at the moment of branching; storage cost accumulates as the branch's data diverges from the parent. There is no separate branch line item, branches roll into your regular database storage and compute totals.

Use case: a development copy of production

Branches also separate daily development from production without maintaining seed scripts. Create a long-lived branch from the production database and point local and staging environments at the branch's hostname. Developers query production-shaped data, and every write stays on the branch, so production is never at risk from a bad migration or a careless DELETE.

Branch data diverges from the parent from the moment of branching. To refresh, delete the branch and create a new one with the same branchId; the new branch starts from the parent's current state. Set a ttl if the branch should clean itself up, or omit it for a permanent development copy.

Use case: per-PR preview database

A CI pipeline that branches on every pull request and tears down on close:

YAML
name: preview-database
on:
pull_request:
types: [opened, reopened, closed]
jobs:
branch:
if: github.event.action != 'closed'
runs-on: ubuntu-latest
steps:
- name: Create branch
run: |
curl -X POST \
-H "X-Appwrite-Project: ${{ vars.APPWRITE_PROJECT_ID }}" \
-H "X-Appwrite-Key: ${{ secrets.APPWRITE_API_KEY }}" \
-H "Content-Type: application/json" \
-d '{"branchId": "pr-${{ github.event.number }}", "ttl": 604800}' \
https://<REGION>.cloud.appwrite.io/v1/postgresql/${{ vars.DATABASE_ID }}/branches || true
teardown:
if: github.event.action == 'closed'
runs-on: ubuntu-latest
steps:
- name: Delete branch
run: |
curl -X DELETE \
-H "X-Appwrite-Project: ${{ vars.APPWRITE_PROJECT_ID }}" \
-H "X-Appwrite-Key: ${{ secrets.APPWRITE_API_KEY }}" \
https://<REGION>.cloud.appwrite.io/v1/postgresql/${{ vars.DATABASE_ID }}/branches/pr-${{ github.event.number }}

The || true on the create call makes the workflow idempotent: if the branch already exists, the call is a no-op.

Was this page helpful?

Share what worked or what we should fix. Once approved, our agents automatically apply suggested updates to the docs.