Skip to content

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
Google 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

  1. Never store remote secrets locally - Use GitHub Secrets exclusively
  2. Unique secrets per environment - SERVICE_AUTH_SECRET must differ
  3. Rotate secrets periodically - Update GitHub Secrets quarterly
  4. Use strong secrets - Generate with openssl rand -base64 32

Operations

  1. Test in staging first - Never deploy directly to production
  2. Use preview for risky changes - Add deploy-preview label to PR
  3. Monitor deployments - Check GitHub Actions for issues
  4. Keep secrets in sync - If updating manually, update GitHub Secrets too

Troubleshooting

"Missing required secret" in CI

  1. Verify secret exists in GitHub: Repository → Settings → Secrets
  2. Check secret name matches exactly (case-sensitive)
  3. Ensure secret is not empty

Authentication fails in deployed function

  1. Verify secrets are set: supabase secrets list --branch <branch>
  2. Check SERVICE_AUTH_SECRET matches between services
  3. Redeploy to refresh secrets

Local development auth fails

  1. Verify .env.local exists
  2. Check Supabase is running: supabase status
  3. Restart: supabase stop && supabase start