Neon's database branching is its most distinctive feature. A branch is an instant copy-on-write clone of your database — schema and data included — that diverges from its parent as you make changes. Create a branch for every pull request, test your migration against real data, and delete it when the PR closes. No separate test database needed.

How Branching Works

Neon branches use copy-on-write: creating a branch is instant regardless of database size because no data is actually copied. Data is shared with the parent until it diverges. This means a branch of a 10 GB database takes milliseconds to create and uses no additional storage until you start writing to it.

Creating Branches

# Install Neon CLI
npm install -g neonctl
neonctl auth
 
# Create a branch from main
neonctl branches create --name preview/pr-42
 
# List branches
neonctl branches list
 
# Get connection string for a branch
neonctl connection-string --branch preview/pr-42
 
# Delete a branch
neonctl branches delete preview/pr-42

Recipe: Automatic Branch per Pull Request (GitHub Actions)

# .github/workflows/preview.yml
name: Preview Database
on:
  pull_request:
    types: [opened, synchronize]
 
jobs:
  create-db-branch:
    runs-on: ubuntu-latest
    steps:
      - uses: neondatabase/create-branch-action@v5
        id: create-branch
        with:
          project_id: ${{ secrets.NEON_PROJECT_ID }}
          branch_name: preview/pr-${{ github.event.pull_request.number }}
          api_key: ${{ secrets.NEON_API_KEY }}
 
      - name: Run migrations on branch
        run: npx drizzle-kit migrate
        env:
          DATABASE_URL: ${{ steps.create-branch.outputs.db_url_with_pooler }}
 
      - name: Output branch URL
        run: echo '${{ steps.create-branch.outputs.db_url_with_pooler }}'
# .github/workflows/cleanup.yml
name: Cleanup Preview Database
on:
  pull_request:
    types: [closed]
 
jobs:
  delete-db-branch:
    runs-on: ubuntu-latest
    steps:
      - uses: neondatabase/delete-branch-action@v3
        with:
          project_id: ${{ secrets.NEON_PROJECT_ID }}
          branch: preview/pr-${{ github.event.pull_request.number }}
          api_key: ${{ secrets.NEON_API_KEY }}

Testing Migrations Against Real Data

The critical value of database branching for AI applications is testing schema changes and new embedding pipelines against real production data — without touching production.

  1. Create a branch from main (inherits all production data)
  2. Point your migration script at the branch connection string
  3. Run the migration — adds new columns, changes types, reindexes embeddings
  4. Test your application against the branch
  5. If anything breaks, delete the branch — production is untouched
  6. Once verified, run the same migration against production main
For embedding schema changes (different model, different dimensions), branching lets you re-embed all documents on a copy of your data before committing to the change in production.

Using Branches for Staging Environments

Create a long-lived branch named staging alongside your main branch. Point your staging deployment at this branch. Changes flow: feature branch → staging branch → main (production). The staging branch can be reset to match main at any time.

# Reset staging branch to match main
neonctl branches reset staging --parent main
 
# This discards all changes on staging and creates a fresh copy of main
# Takes milliseconds regardless of database size
Metadata Value
Title Neon Database Branching: Isolated Environments for Every Pull Request
Tool Neon
Primary SEO keyword neon database branching
Secondary keywords neon branch per PR, neon preview environment, neon github actions, neon staging
Estimated read time 7 minutes
Research date 2026-04-14