Multi-Environment Branching Setup Guide¶
This guide covers the setup for multi-environment deployment using Supabase preview branches.
Branch Types: Persistent vs Ephemeral¶
Supabase branches in this project are managed as two types:
Persistent Branches (Never Deleted)¶
| Branch | Type | Lifecycle | Data |
|---|---|---|---|
main |
Production | Always active | Preserved forever |
develop |
Staging | Pausable, auto-unpause on push | Preserved across pauses |
Characteristics: - Created once and reused indefinitely - Database state is preserved - Secrets are set once and persist - Staging is paused (not deleted) when inactive to save resources - Staging is automatically unpaused when code is pushed
Ephemeral Branches (Deleted After Use)¶
| Branch | Type | Lifecycle | Data |
|---|---|---|---|
pr-* |
Preview | Created per-PR, deleted on close | Lost on deletion |
Characteristics:
- Created when PR is labeled with deploy-preview
- Deleted when PR is merged or closed
- Database and secrets are destroyed on deletion
- Each PR gets isolated testing environment
- Auto-generated unique secrets for security isolation
Architecture Overview¶
Single Supabase Project: $SUPABASE_PROJECT_ID
│
├── main branch (production) [PERSISTENT - ALWAYS ACTIVE]
│ └── Git: main → Deploy to Supabase main branch
│ └── GitHub environment: production
│ └── Secrets set by: deploy-production.yml
│ └── Never paused, never deleted
│
├── develop branch (staging) [PERSISTENT - PAUSABLE]
│ └── Git: develop → Deploy to Supabase develop branch
│ └── GitHub environment: staging
│ └── Secrets set by: deploy-staging.yml
│ └── Auto-paused after 24h of inactivity (pause-staging.yml)
│ └── Auto-unpaused on next push to develop
│ └── NEVER deleted - data is preserved
│
└── pr-* branches (preview) [EPHEMERAL]
└── Git: PR branches → Create temp Supabase branches
└── GitHub environment: preview-pr-{number}
└── Secrets set by: pr-checks.yml
└── DELETED when PR closed (cleanup-preview.yml)
└── Data is NOT preserved
Branch Lifecycle Summary¶
| Branch | Type | Created | Paused | Deleted |
|---|---|---|---|---|
main |
Persistent | Always exists | Never | Never |
develop |
Persistent | On first push | After 24h inactivity | Never |
pr-* |
Ephemeral | On PR with deploy-preview label |
Never | On PR close |
Prerequisites¶
- Supabase CLI installed (
npm i -g supabase) - Supabase access token (from Dashboard → Account → Access Tokens)
- Project ID (from Dashboard → Settings → General → Reference ID)
- GitHub repository with Actions enabled
One-Time Setup¶
1. Authenticate with Supabase CLI¶
2. Link Your Project¶
3. Configure GitHub Secrets¶
Add the required secrets to your GitHub repository (see GitHub Secrets section below).
4. Push to Develop to Create Staging Branch¶
The staging branch is created automatically on first push to develop:
This triggers deploy-staging.yml which:
1. Creates the develop branch in Supabase (if not exists)
2. Applies migrations
3. Sets branch secrets
4. Deploys Edge Functions
5. Seed Staging Data (Optional)¶
GitHub Repository Setup¶
Required Secrets¶
Repository Secrets (Settings → Secrets and variables → Actions → Repository secrets):
| Secret | Description | Where to Find |
|---|---|---|
SUPABASE_ACCESS_TOKEN |
CLI authentication token | Dashboard → Account → Access Tokens |
SUPABASE_PROJECT_ID |
Project reference ID | Dashboard → Settings → General |
SUPABASE_PUBLISHABLE_KEY |
Public API key (for smoke tests) | Dashboard → Settings → API |
Environment Secrets (Settings → Environments → {environment name}):
| Environment | Secret | Description | How to Generate |
|---|---|---|---|
production |
SERVICE_AUTH_SECRET_PRODUCTION |
Production service-to-service auth | openssl rand -base64 32 |
staging |
SERVICE_AUTH_SECRET_STAGING |
Staging service-to-service auth (also seeds PR secrets) | openssl rand -base64 32 |
Note: PR preview branches auto-generate unique secrets—no manual setup needed.
Understanding Supabase Branching¶
API Keys are PROJECT-level:
- SUPABASE_PUBLISHABLE_KEY is the same for all branches
- You only need ONE publishable key in GitHub repository secrets
- Used by smoke tests and health checks to verify deployments
Database is BRANCH-level: - Each branch (main, develop, pr-) has its own *isolated database instance** - Migrations are applied per-branch - Data does not leak between branches
Secrets are BRANCH-level:
- Application secrets like SERVICE_AUTH_SECRET are set per-branch
- CI/CD automatically sets secrets during deployment
Branch-to-Secret Mapping¶
| Branch Type | Git Branch | Supabase Branch | GitHub Environment | SERVICE_AUTH_SECRET |
|---|---|---|---|---|
| Production | main |
main |
production |
From SERVICE_AUTH_SECRET_PRODUCTION |
| Staging | develop |
develop |
staging |
From SERVICE_AUTH_SECRET_STAGING |
| PR Preview | feat/*, etc. |
pr-{number} |
preview-pr-{number} |
Auto-generated per PR |
How Secrets Are Set (Automatic via CI/CD)¶
Production (on push to main):
# deploy-production.yml automatically runs:
supabase secrets set \
ENVIRONMENT=production \
LOG_LEVEL=info \
SERVICE_AUTH_SECRET="${{ secrets.SERVICE_AUTH_SECRET_PRODUCTION }}"
Staging (on push to develop):
# deploy-staging.yml automatically runs:
supabase secrets set --branch develop \
ENVIRONMENT=staging \
LOG_LEVEL=debug \
SERVICE_AUTH_SECRET="${{ secrets.SERVICE_AUTH_SECRET_STAGING }}"
Preview (on PR with deploy-preview label):
# pr-checks.yml automatically runs:
# Generates unique secret per PR
supabase secrets set --branch pr-N \
ENVIRONMENT=preview \
LOG_LEVEL=debug \
SERVICE_AUTH_SECRET="<auto-generated>"
GitHub Environments¶
Create these environments in your repository (Settings → Environments):
- production
- Protection rules: Require reviewers
-
Deployment branches:
mainonly -
staging
- No protection rules needed
-
Deployment branches:
developonly -
preview-pr-* (dynamic)
- Created automatically by workflows
Git Branch Strategy¶
Main Branches¶
main- Production code, deploys to Supabase main branchdevelop- Staging code, deploys to Supabase develop branch
Feature Branches¶
# Create feature branch from develop
git checkout develop
git checkout -b feat/my-feature
# Work on feature...
git add .
git commit -m "Add my feature"
# Push and create PR to develop
git push -u origin feat/my-feature
# Create PR on GitHub
Preview Deployments¶
- Create a PR to
developormain - Add the
deploy-previewlabel to the PR - A preview branch is automatically created with secrets
- Preview is deleted when PR is closed
Deployment Workflows¶
Automatic Deployments¶
| Trigger | Target | Workflow | Action |
|---|---|---|---|
Push to main |
Production | deploy-production.yml |
Deploy + set secrets |
Push to develop |
Staging | deploy-staging.yml |
Create/unpause + deploy |
PR with deploy-preview label |
Preview | pr-checks.yml |
Create + deploy |
| PR closed | PR preview | cleanup-preview.yml |
Delete branch |
| Daily (midnight UTC) | Staging | pause-staging.yml |
Pause if inactive 24h |
Branch States¶
Staging Branch (develop):
- Active: Running and serving requests
- Paused: Stopped to save resources, data preserved
- Automatically unpaused when you push to develop
- Manually unpause: supabase branches unpause develop
PR Branches (pr-*):
- Created when PR has deploy-preview label
- Deleted when PR is merged or closed
- Never paused (short-lived)
Manual Deployments¶
# Deploy to staging
deno task deploy --branch develop
# Deploy to production (main branch)
deno task deploy
# Deploy specific function to staging
deno task deploy graph --branch develop
Health Checks¶
Verify Deployments¶
# Check staging health
curl https://YOUR_PROJECT.supabase.co/functions/v1/health/health/live
curl https://YOUR_PROJECT.supabase.co/functions/v1/health/health/ready
# Run smoke tests
deno task smoke:staging
deno task smoke:production
Managing Branch States¶
Check Branch Status¶
# List all branches and their status
supabase branches list
# Get detailed info about staging branch
supabase branches get develop --output json
Manually Pause/Unpause Staging¶
# Pause staging to save resources
supabase branches pause develop
# Unpause staging (or just push to develop)
supabase branches unpause develop
Force Pause via GitHub Actions¶
Verifying Secrets¶
# List production secrets
supabase secrets list
# List staging secrets
supabase secrets list --branch develop
# List preview secrets
supabase secrets list --branch pr-123
Rollback Procedures¶
Rollback Functions¶
# List recent deployments
deno task rollback:list
# Rollback all functions to specific commit
deno task rollback abc1234
# Rollback specific function
deno task rollback graph abc1234
# Dry run (preview without applying)
deno task rollback --dry-run abc1234
Database Rollback¶
Database migrations are forward-only. For rollbacks:
- Create a new migration that reverts changes
- Push to the affected branch
# Create rollback migration
deno task db:new "rollback_feature_x"
# Edit the migration file to undo changes
# Push to staging
supabase db push --branch develop
Troubleshooting¶
Staging Branch is Paused¶
If staging returns errors, it might be paused:
# Check branch status
supabase branches get develop --output json
# Unpause manually
supabase branches unpause develop
# Or push to develop (auto-unpauses)
git commit --allow-empty -m "chore: unpause staging"
git push origin develop
Branch Not Found¶
# List available branches
supabase branches list
# Create staging branch (push to develop)
git push origin develop
# Or create manually
supabase branches create develop
Secrets Not Applied¶
Secrets are set automatically by CI/CD. If missing:
# Verify secrets on branch
supabase secrets list --branch develop
# Trigger redeployment
git commit --allow-empty -m "chore: refresh secrets"
git push origin develop
Health Check Failing¶
# Check if branch is paused first
supabase branches get develop --output json
# If paused, unpause
supabase branches unpause develop
# Check function logs
supabase functions logs health --branch develop
# Verify database connectivity
curl https://YOUR_PROJECT.supabase.co/functions/v1/health/health/ready
Preview Branch Issues¶
# List all preview branches
supabase branches list
# Manually delete stuck preview branch
supabase branches delete pr-123 --confirm
Staging Won't Unpause¶
If the staging branch fails to unpause:
# Check for errors
supabase branches get develop --output json
# Wait and retry (branches can take time to unpause)
sleep 60
supabase branches unpause develop
# Check Supabase dashboard for branch health
# Dashboard → Database → Branches → develop
Note: The staging branch is PERSISTENT and should never be deleted. Deleting it will lose all staging data. If you must recreate it, coordinate with the team first.
Best Practices¶
Branch Management¶
- Never delete persistent branches -
mainanddevelopare persistent; onlypr-*branches should be deleted - Use ephemeral PR branches for risky changes - They're deleted after PR close, no cleanup needed
- Let staging auto-pause - Don't manually pause; let the scheduled workflow handle it
Development Workflow¶
- Always test in staging first - Never deploy directly to production
- Keep migrations small - Easier to rollback if needed
- Use
deploy-previewlabel sparingly - PR branches consume Supabase resources
Security¶
- Never store remote secrets locally - Let CI/CD handle secret management
- Rotate secrets periodically - Update GitHub Secrets quarterly
- Review deployment logs - Check GitHub Actions for issues
Monitoring¶
- Monitor health endpoints - Set up alerts for production
- Check branch status regularly - Ensure staging isn't stuck paused