| name | neon-branching |
| description | Manage Neon serverless Postgres database branches: branch-per-PR workflow, neon branch create/delete, connection strings, reset branch from parent. Trigger: when using Neon database, neon branch create, neon branch delete, Neon branching, database branching, branch-per-PR, Neon Postgres |
| version | 1 |
| argument-hint | [branch create|delete|list|get] [--name branch-name] [--project-id proj-id] |
| allowed-tools | ["bash","read","write","grep","glob"] |
Neon Database Branching
You are now operating in Neon serverless Postgres branching mode.
Installation and Setup
npm install -g neonctl
brew install neonctl
neonctl auth
neonctl auth --token $NEON_API_KEY
neonctl me
neonctl set-context --project-id <project-id>
neonctl projects list
Branch Management
neonctl branches list
neonctl branches list --project-id <project-id>
neonctl branches get main
neonctl branches get my-feature-branch
neonctl branches create --name my-feature-branch
neonctl branches create --name feature-db-v2 --parent staging
neonctl branches create \
--name restore-to-yesterday \
--parent main \
--parent-timestamp 2024-01-15T10:00:00Z
neonctl branches create \
--name restore-to-lsn \
--parent main \
--parent-lsn 0/18B5F28
neonctl branches delete my-feature-branch
neonctl branches rename my-feature-branch --name my-renamed-branch
neonctl branches set-primary my-branch
neonctl branches reset my-feature-branch --parent
Connection Strings
neonctl connection-string my-feature-branch
neonctl connection-string main
neonctl connection-string my-branch \
--role-name myuser \
--database-name mydb
neonctl connection-string my-branch --pooled
neonctl connection-string my-branch --output env
BRANCH_DB_URL=$(neonctl connection-string my-feature-branch)
export DATABASE_URL="$BRANCH_DB_URL"
neonctl connection-string my-branch --output json | jq '{
host: .host,
port: .port,
database: .dbname,
user: .user,
password: .password
}'
Branch-per-PR Workflow
This is the primary Neon use case: create a fresh database branch for each pull request, run migrations, test, then delete the branch when the PR closes.
Manual Workflow
PR_NUMBER=42
BRANCH_NAME="pr-${PR_NUMBER}"
neonctl branches create --name "$BRANCH_NAME" --parent main
DB_URL=$(neonctl connection-string "$BRANCH_NAME")
export DATABASE_URL="$DB_URL"
npm run db:migrate
npm test
neonctl branches delete "$BRANCH_NAME"
GitHub Actions Automation
name: Preview Database Branch
on:
pull_request:
types: [opened, synchronize, reopened, closed]
jobs:
setup-db-branch:
if: github.event.action != 'closed'
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Install Neon CLI
run: npm install -g neonctl
- name: Create database branch
id: create-branch
env:
NEON_API_KEY: ${{ secrets.NEON_API_KEY }}
PROJECT_ID: ${{ vars.NEON_PROJECT_ID }}
run: |
BRANCH_NAME="pr-${{ github.event.number }}"
neonctl branches delete "$BRANCH_NAME" 2>/dev/null || true
neonctl branches create \
--name "$BRANCH_NAME" \
--parent main \
--project-id "$PROJECT_ID"
DB_URL=$(neonctl connection-string "$BRANCH_NAME" \
--project-id "$PROJECT_ID" \
--role-name neondb_owner \
--database-name neondb)
echo "db_url=$DB_URL" >> $GITHUB_OUTPUT
echo "branch_name=$BRANCH_NAME" >> $GITHUB_OUTPUT
- name: Run migrations
env:
DATABASE_URL: ${{ steps.create-branch.outputs.db_url }}
run: npm run db:migrate
- name: Run tests
env:
DATABASE_URL: ${{ steps.create-branch.outputs.db_url }}
run: npm test
- name: Comment PR with branch URL
uses: actions/github-script@v7
with:
script: |
github.rest.issues.createComment({
issue_number: context.issue.number,
owner: context.repo.owner,
repo: context.repo.repo,
body: '🗄️ Preview database branch: `${{ steps.create-branch.outputs.branch_name }}`'
})
cleanup-db-branch:
if: github.event.action == 'closed'
runs-on: ubuntu-latest
steps:
- name: Delete database branch
env:
NEON_API_KEY: ${{ secrets.NEON_API_KEY }}
PROJECT_ID: ${{ vars.NEON_PROJECT_ID }}
run: |
npm install -g neonctl
BRANCH_NAME="pr-${{ github.event.number }}"
neonctl branches delete "$BRANCH_NAME" \
--project-id "$PROJECT_ID" || true
Vercel Integration Pattern
vercel env add DATABASE_URL preview "$(neonctl connection-string pr-42)"
Compute Endpoints
Each branch can have multiple compute endpoints. By default, endpoints use autosuspend.
neonctl endpoints list --branch my-feature-branch
neonctl endpoints create \
--branch my-feature-branch \
--type read_only
neonctl endpoints get <endpoint-id>
neonctl endpoints start <endpoint-id>
neonctl endpoints suspend <endpoint-id>
neonctl endpoints delete <endpoint-id>
neonctl endpoints update <endpoint-id> --cu-min 0.25 --cu-max 2
Database and Role Management
neonctl databases list --branch my-feature-branch
neonctl databases create mydb --branch my-feature-branch
neonctl databases delete mydb --branch my-feature-branch
neonctl roles list --branch my-feature-branch
neonctl roles create myuser --branch my-feature-branch
neonctl roles get myuser --branch my-feature-branch
Point-in-Time Restore
neonctl branches create \
--name restored-20240115 \
--parent main \
--parent-timestamp 2024-01-15T10:00:00Z
neonctl branches reset production --parent restored-20240115
neonctl branches rename production --name production-backup
neonctl branches rename restored-20240115 --name production
Using Neon via REST API
NEON_API="https://console.neon.tech/api/v2"
AUTH="Authorization: Bearer $NEON_API_KEY"
curl -s -H "$AUTH" \
"$NEON_API/projects/$PROJECT_ID/branches" | jq '.branches[] | {id, name, created_at}'
curl -s -X POST -H "$AUTH" -H "Content-Type: application/json" \
"$NEON_API/projects/$PROJECT_ID/branches" \
-d '{
"branch": {"name": "my-branch", "parent_id": "main-branch-id"},
"endpoints": [{"type": "read_write"}]
}' | jq .
curl -s -X DELETE -H "$AUTH" \
"$NEON_API/projects/$PROJECT_ID/branches/$BRANCH_ID"
curl -s -H "$AUTH" \
"$NEON_API/projects/$PROJECT_ID/connection_uri?branch_id=$BRANCH_ID&role_name=neondb_owner&database_name=neondb" | \
jq -r '.uri'
Cost Management
neonctl endpoints list | jq '.endpoints[] | {id, current_state, autosuspend_duration_seconds}'
neonctl branches get my-branch | jq '.branch.logical_size'
neonctl projects get $PROJECT_ID | jq '.project | {
data_storage_bytes_hour: .data_storage_bytes_hour,
compute_time_seconds: .compute_time_seconds
}'
Troubleshooting
neonctl --debug branches list
neonctl --version
neonctl auth
neonctl projects list | jq '.projects[] | {id, name}'