Multi-Environment Configuration Reference¶
Complete reference for multi-environment deployment with Supabase branches.
Architecture Overview¶
┌─────────────────────────────────────────────────────────────────────────┐
│ DEPLOYMENT ARCHITECTURE │
├─────────────────────────────────────────────────────────────────────────┤
│ │
│ Git Branch Supabase Branch GitHub Environment │
│ ────────── ─────────────── ────────────────── │
│ main ──────► main (production) ──► production │
│ develop ──────► develop (staging) ──► staging │
│ feature/* ──────► pr-* (ephemeral) ──► preview-pr-{number} │
│ │
├─────────────────────────────────────────────────────────────────────────┤
│ │
│ LOCAL DEVELOPMENT │
│ ───────────────── │
│ supabase start ──► Local Docker stack ──► .env.local │
│ │
└─────────────────────────────────────────────────────────────────────────┘
Secrets Management Philosophy¶
Key Principle: Remote environment secrets are managed exclusively via CI/CD.
| Environment | Secret Storage | Set By |
|---|---|---|
| Local | .env.local file |
Developer |
| Staging | GitHub Secrets → CI/CD | deploy-staging.yml |
| Production | GitHub Secrets → CI/CD | deploy-production.yml |
| Preview (PR) | GitHub Secrets → CI/CD | pr-checks.yml |
Developers never need access to staging or production secrets locally.
Configuration Files¶
Files Overview¶
| File | Committed | Purpose |
|---|---|---|
supabase/config.toml |
✅ Yes | Supabase local development config |
deno.json |
✅ Yes | Deno tasks and imports |
.env.local.example |
✅ Yes | Template for local development |
.env.local |
❌ No | Actual local secrets (gitignored) |
supabase/config.toml¶
Main Supabase configuration for local development.
[project]
# Project ID is set via environment or supabase link
[api]
enabled = true
port = 54321
[db]
port = 54322
major_version = 15
[studio]
enabled = true
port = 54323
[auth]
enabled = true
site_url = "http://localhost:3000"
# Local OAuth (optional - requires separate OAuth apps for localhost)
[auth.external.github]
enabled = true
client_id = "env(GITHUB_CLIENT_ID)"
secret = "env(GITHUB_CLIENT_SECRET)"
# Edge Functions
[functions.health]
verify_jwt = false
entrypoint = "../functions/health/index.ts"
import_map = "../deno.json"
[functions.graph]
verify_jwt = false
entrypoint = "../functions/graph/index.ts"
import_map = "../deno.json"
.env.local.example¶
Template for local development (copy to .env.local):
# ============================================
# LOCAL SUPABASE (defaults work out of box)
# ============================================
SUPABASE_URL=http://127.0.0.1:54321
# Default local keys (safe to use, automatically set by supabase start)
SUPABASE_PUBLISHABLE_KEY=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...
SUPABASE_SECRET_KEY=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...
# ============================================
# APPLICATION SECRETS
# ============================================
SERVICE_AUTH_SECRET=local-dev-secret-any-value-works
ENVIRONMENT=local
LOG_LEVEL=debug
# ============================================
# OAUTH (Optional - for local OAuth testing)
# ============================================
# Create separate OAuth apps for localhost!
# Callback: http://localhost:54321/auth/v1/callback
GITHUB_CLIENT_ID=your-local-github-client-id
GITHUB_CLIENT_SECRET=your-local-github-client-secret
Environment Variables¶
Variable Scope¶
┌─────────────────────────────────────────────────────────────────┐
│ PROJECT-LEVEL │
│ (Same value across all Supabase branches) │
├─────────────────────────────────────────────────────────────────┤
│ SUPABASE_URL → Project URL │
│ SUPABASE_PUBLISHABLE_KEY → Anonymous API key │
│ SUPABASE_SECRET_KEY → Admin API key │
│ SUPABASE_PROJECT_ID → Project reference ID │
└─────────────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────────┐
│ BRANCH-LEVEL │
│ (Different value per environment) │
├─────────────────────────────────────────────────────────────────┤
│ SERVICE_AUTH_SECRET → MUST be unique per branch │
│ ENVIRONMENT → local / staging / production / preview │
│ LOG_LEVEL → debug / info │
└─────────────────────────────────────────────────────────────────┘
Variable Reference¶
| Variable | Local | Staging | Production | Preview |
|---|---|---|---|---|
SUPABASE_URL |
localhost:54321 | Project URL | Project URL | Project URL |
SUPABASE_PUBLISHABLE_KEY |
Local default | Project key | Project key | Project key |
SERVICE_AUTH_SECRET |
Any value | Unique | Unique | Auto-generated |
ENVIRONMENT |
local |
staging |
production |
preview |
LOG_LEVEL |
debug |
debug |
info |
debug |
GitHub Secrets¶
Required Secrets¶
Configure in: Repository → Settings → Secrets and variables → Actions
| Secret | Required | Description |
|---|---|---|
SUPABASE_ACCESS_TOKEN |
✅ | CLI authentication token |
SUPABASE_PROJECT_ID |
✅ | Project reference ID |
SUPABASE_PUBLISHABLE_KEY |
✅ | Project anonymous API key |
SERVICE_AUTH_SECRET_PRODUCTION |
✅ | Production service auth secret |
SERVICE_AUTH_SECRET_STAGING |
✅ | Staging service auth secret |
How to Obtain¶
# SUPABASE_ACCESS_TOKEN
# supabase.com → Account (top right) → Access Tokens → Generate
# SUPABASE_PROJECT_ID
# Dashboard → Settings → General → Reference ID
# Dashboard → Settings → Database → Connection string (extract password)
# SUPABASE_PUBLISHABLE_KEY
# Dashboard → Settings → API → Project API keys → anon public
# SERVICE_AUTH_SECRET_PRODUCTION / SERVICE_AUTH_SECRET_STAGING
# Generate with: openssl rand -base64 32
GitHub Environments¶
Configure in: Repository → Settings → Environments
| Environment | Branch | Protection Rules |
|---|---|---|
production |
main |
Required reviewers |
staging |
develop |
None |
preview-pr-* |
PR branches | Auto-created |
How Secrets Flow to Supabase¶
Automatic (via GitHub Actions)¶
When you push code, GitHub Actions automatically sets Supabase branch secrets:
Production (main branch):
# deploy-production.yml sets:
supabase secrets set \
ENVIRONMENT=production \
LOG_LEVEL=info \
SERVICE_AUTH_SECRET="${{ secrets.SERVICE_AUTH_SECRET_PRODUCTION }}"
Staging (develop branch):
# deploy-staging.yml sets:
supabase secrets set --branch develop \
ENVIRONMENT=staging \
LOG_LEVEL=debug \
SERVICE_AUTH_SECRET="${{ secrets.SERVICE_AUTH_SECRET_STAGING }}"
Preview (PR branches):
# pr-checks.yml sets:
# Generates unique secret per PR by hashing branch name + staging secret
supabase secrets set --branch pr-N \
ENVIRONMENT=preview \
LOG_LEVEL=debug \
SERVICE_AUTH_SECRET="<auto-generated>"
Manual Override (Emergency Only)¶
If you need to manually set secrets (not recommended for regular use):
# Link project first
supabase link --project-ref <project-id>
# Set production secrets
supabase secrets set \
ENVIRONMENT=production \
SERVICE_AUTH_SECRET="your-secret"
# Set staging secrets
supabase secrets set --branch develop \
ENVIRONMENT=staging \
SERVICE_AUTH_SECRET="your-secret"
# Verify
supabase secrets list
supabase secrets list --branch develop
Supabase Dashboard Configuration¶
OAuth Providers (One-time Setup)¶
Configure in Dashboard → Authentication → Providers:
| Provider | Where to Configure | Callback URL |
|---|---|---|
| GitHub | Supabase Dashboard | https://{project-id}.supabase.co/auth/v1/callback |
| Supabase Dashboard | https://{project-id}.supabase.co/auth/v1/callback |
OAuth configuration applies to all branches (project-level).
Setup Checklist¶
Initial Setup (One-time)¶
# 1. Install Supabase CLI
npm install -g supabase
# 2. Login to Supabase
supabase login
# 3. Link project
supabase link --project-ref <your-project-id>
# 4. Create persistent staging branch
supabase branches create develop --persistent
# 5. Add GitHub Secrets (see table above)
# Repository → Settings → Secrets → Actions
Local Development¶
# 1. Copy template
cp .env.local.example .env.local
# 2. Start Supabase
supabase start
# 3. Start dev server
deno task dev
Deployment¶
# Push to trigger automatic deployment
git push origin main # → production
git push origin develop # → staging
# Or trigger manually in GitHub Actions
Best Practices¶
Security¶
- Never store remote secrets locally - Use GitHub Secrets exclusively
- Unique secrets per environment - SERVICE_AUTH_SECRET must differ
- Rotate secrets periodically - Update GitHub Secrets quarterly
- Use strong secrets - Generate with
openssl rand -base64 32
Operations¶
- Test in staging first - Never deploy directly to production
- Use preview for risky changes - Add
deploy-previewlabel to PR - Monitor deployments - Check GitHub Actions for issues
- Keep secrets in sync - If updating manually, update GitHub Secrets too
Troubleshooting¶
"Missing required secret" in CI¶
- Verify secret exists in GitHub: Repository → Settings → Secrets
- Check secret name matches exactly (case-sensitive)
- Ensure secret is not empty
Authentication fails in deployed function¶
- Verify secrets are set:
supabase secrets list --branch <branch> - Check SERVICE_AUTH_SECRET matches between services
- Redeploy to refresh secrets
Local development auth fails¶
- Verify
.env.localexists - Check Supabase is running:
supabase status - Restart:
supabase stop && supabase start
Related Documentation¶
- Branching Setup - Branch strategy
- Secrets Management - Detailed secrets guide
- CI/CD Guide - GitHub Actions workflows
- OAuth Setup - OAuth provider configuration