Skip to main content

Documentation Index

Fetch the complete documentation index at: https://docs.encoreos.io/llms.txt

Use this file to discover all available pages before exploring further.

Version: 2.2.1 Last Updated: 2026-04-24 Based on: Supabase Official Documentation This guide provides recommendations for configuring supabase/config.toml to support four environments:
  • Local - Local CLI development (Docker)
  • Dev - Persistent dev project (zkgxozahyczcnzpwhbbf); per-PR preview DBs created by Branching from PRs targeting GitHub prod
  • Staging (Persistent) - UAT and pre-prod validation
  • Production - Live application (srcaoozjkrughebmbvfb); per-PR preview DBs created by Branching only for release PRs targeting GitHub production
Authoritative branch ↔ Vercel ↔ Supabase mapping: see VERCEL_SUPABASE_ENV_ALIGNMENT.md. When that document and this one disagree, the alignment doc wins.
2026-04 reconcile: the legacy production project (zkgxozahyczcnzpwhbbf) was repurposed as Dev. Two new clean projects were created from a single optimized baseline migration. See SUPABASE_RECONCILE_2026-04.md for the cutover record.
As of 2026-04-22 — source of truth and deploy path
  • Dev (zkgxozahyczcnzpwhbbf, “Encore OS”): Lovable + Supabase MCP apply migrations and deploy Edge Functions here first; this database is the live schema reference for day-to-day work.
  • Git repo (supabase/migrations/, supabase/functions/): canonical history for review and for production rollout.
  • Production (srcaoozjkrughebmbvfb, “encore_os_prod”): Supabase Branching (GitHub integration) replays migrations for release PRs and, with Deploy to production ON, applies them on merge to production. CI still runs supabase db push --yes from .github/workflows/supabase-deploy-prod.yml on the same push as a reconcile with the repo. Break-glass: manual workflow_dispatch on that workflow with skip_db_push when migrations were already fully applied elsewhere (document in your ops log).
Do not point ad-hoc db push at Dev for schema promotion; treat Dev as MCP-driven. Use GitHub prod → optional .github/workflows/supabase-deploy-dev.yml backstop, and production → prod.

Bidirectional sync (Dev ↔ repo)

Two paths keep Dev and the repo aligned:
  • Repo → Dev (push): Author routine DDL declaratively under supabase/schemas/, then run npm run db:schemas:diff -- -f <slug> (wraps supabase stop + supabase db diff -f <slug>) to emit the migration in supabase/migrations/. Reserve supabase migration new for caveat migrations (DML, certain ALTER POLICY / view ownership / grant / comment cases — see DECLARATIVE_SCHEMA_GUIDE.md and MIGRATION_LANES.md). The migrations land on the Dev project when supabase-deploy-dev.yml runs on its branch trigger (see workflow table below).
  • Dev → repo (pull): Migrations applied directly to Dev by Lovable / Supabase MCP are captured back into supabase/migrations/ by supabase-sync-from-dev.yml. It runs daily at 06:00 UTC and on-demand via workflow_dispatch. The workflow runs supabase db diff --linked -f <slug> to produce a real timestamped migration, validates it with db reset + the RLS / FK / lane guards, refreshes the declarative tree under supabase/schemas/ via npm run db:schemas:export when bootstrapped (so the repo’s source of truth stays current), regenerates src/integrations/supabase/types.ts, and opens a review PR to dev. Developers can run the same flow locally with npm run db:pull-from-dev -- --name <slug>.

Promotion path

Local (Docker)  ->  GitHub `prod` / Dev project  ->  GitHub `production` / Prod project
        |                      |                              |
        +-- migration tests    +-- Lovable + MCP (primary)    +-- Branching preview (required check) → merge
        |                      |                              +-- CI db push + functions deploy (reconcile; break-glass skip_db_push)
Release PRs (prodproduction): Require green Supabase Preview on encore_os_prod before merge. Order of operations and branch protection are documented in VERCEL_SUPABASE_ENV_ALIGNMENT.md section 2.3. CLI spike notes: BRANCHING_CI_SPIKE.md. Optional persistent staging (separate Supabase project) can sit between Dev and Prod when provisioned; there is no shared staging ref in config.toml until you add one.
This approach uses Supabase’s [remotes] configuration blocks to define environment-specific settings. This is the recommended approach per Supabase documentation.
# supabase/config.toml (illustrative — see repo file for the live copy)

# Default configuration (used for local development)
# project_id is implied for `supabase start`

[api]
enabled = true
port = 54321
schemas = ["public", "storage", "graphql_public"]

[db]
port = 54322

# Production remote — encore_os_prod
[remotes.production]
project_id = "srcaoozjkrughebmbvfb"

[remotes.production.api]
max_rows = 500

# Dev remote — Encore OS (Lovable / MCP target)
[remotes.dev]
project_id = "zkgxozahyczcnzpwhbbf"

[remotes.dev.api]
max_rows = 1000

# Optional: add [remotes.staging] when a dedicated UAT project exists.

Option 2: Environment Variable-Based (Alternative)

If you prefer to switch environments via environment variables:
# supabase/config.toml

# Use environment variable to determine project
project_id = "env(SUPABASE_PROJECT_ID)"

[api]
enabled = true
port = 54321
schemas = ["public", "storage", "graphql_public"]

[db]
port = 54322
pool_size = 10
Then use different .env files:
  • .env.localSUPABASE_PROJECT_ID=local-dev
  • .env.stagingSUPABASE_PROJECT_ID=<staging-ref>
  • .env.productionSUPABASE_PROJECT_ID=srcaoozjkrughebmbvfb
Note: Option 1 is preferred because it provides better separation and allows environment-specific configurations.

Workflow by Environment

Local Development

Purpose: Feature development, migration testing, rapid iteration Setup:
# Start local Supabase stack
supabase start

# This uses the default config (no remote specified)
# Local services available at:
# - API: http://localhost:54321
# - Studio: http://localhost:54323
# - DB: localhost:54322
Commands:
# Create new migration
supabase migration new my_feature

# Apply migrations locally
supabase db reset  # Resets and applies all migrations
# OR
supabase migration up  # Applies pending migrations

# Test migrations
supabase db diff  # See what would change

# Generate types
supabase gen types typescript --local > src/integrations/supabase/types.ts
Configuration: Uses default [api], [db] settings in config.toml

Staging (Persistent UAT)

Purpose: User acceptance testing, integration testing, pre-production validation Setup:
# Link to staging project
supabase link --project-ref <STAGING_PROJECT_REF>

# Verify connection
supabase status
Deploying to Staging:
# Push migrations to staging
supabase db push

# Deploy edge functions
supabase functions deploy

# Set secrets (if needed)
supabase secrets set --env-file .env.staging
Configuration: Uses [remotes.staging] block from config.toml Best Practices:
  • Keep staging schema in sync with production
  • Use synthetic test data (no PHI/PII)
  • Test all migrations in staging before production
  • Use staging for UAT sign-offs

Production

Purpose: Live application serving real users Canonical mapping: GitHub branch production, project ref srcaoozjkrughebmbvfb — see VERCEL_SUPABASE_ENV_ALIGNMENT.md. Setup:
# Link to production project (encore_os_prod)
supabase link --project-ref srcaoozjkrughebmbvfb

# Verify connection (be careful!)
supabase status
Deploying to Production:
# ⚠️ ALWAYS test in staging first!

# Push migrations to production
supabase db push

# Deploy edge functions
supabase functions deploy

# Set secrets (if needed)
supabase secrets set --env-file .env.production
Configuration: Uses [remotes.production] block from config.toml Best Practices:
  • NEVER deploy untested migrations to production
  • Always deploy to staging first
  • Use production for verified, tested changes only
  • Monitor deployments closely
  • Have rollback plan ready

Environment Switching Workflow

# Local development (default)
git checkout feature/my-feature
supabase start  # Uses local config

# Staging deployment
git checkout staging
supabase link --project-ref <STAGING_PROJECT_REF>
supabase db push

# Production deployment (GitHub branch `production`, not `prod`)
git checkout production
supabase link --project-ref srcaoozjkrughebmbvfb
supabase db push

Alternative: Manual Linking

# Switch to staging
supabase link --project-ref <STAGING_PROJECT_REF>

# Switch to production (encore_os_prod)
supabase link --project-ref srcaoozjkrughebmbvfb

# Switch back to local
supabase unlink  # Then use supabase start for local

Configuration Options Reference

API Configuration

[api]
enabled = true
port = 54321
schemas = ["public", "storage", "graphql_public"]
extra_search_path = ["public", "extensions"]
max_rows = 1000  # Limit rows returned per request

Database Configuration

[db]
port = 54322
pool_size = 10  # Connection pool size
major_version = 15  # Postgres version

Remote-Specific Overrides

[remotes.staging]
project_id = "<STAGING_PROJECT_REF>"

# Override API settings for staging
[remotes.staging.api]
max_rows = 2000  # Staging can handle more rows

# Override DB settings for staging
[remotes.staging.db]
pool_size = 20

Secrets Management

Local Development

Secrets are loaded from .env file in project root:
# .env (gitignored)
SUPABASE_AUTH_GITHUB_CLIENT_ID="local-dev-id"
SUPABASE_AUTH_GITHUB_SECRET="local-dev-secret"
Reference in config.toml:
[auth.external.github]
enabled = true
client_id = "env(SUPABASE_AUTH_GITHUB_CLIENT_ID)"
secret = "env(SUPABASE_AUTH_GITHUB_SECRET)"

Remote Environments (Staging/Production)

Set secrets via CLI:
# Set secrets for linked project
supabase secrets set GITHUB_CLIENT_ID=xxx GITHUB_SECRET=yyy

# Or from env file
supabase secrets set --env-file .env.staging
Important: Secrets are project-specific. You must set them separately for staging and production.

Migration Workflow

1. Local Development
   └─> Create migration locally
   └─> Test with `supabase db reset`
   └─> Commit to feature branch

2. Staging Deployment
   └─> Merge to staging branch
   └─> Link to staging: `supabase link --project-ref <STAGING_PROJECT_REF>`
   └─> Push migrations: `supabase db push`
   └─> Verify in staging dashboard

3. Production Deployment
   └─> Merge to `production` branch (release PR from `prod`)
   └─> Link to production: `supabase link --project-ref srcaoozjkrughebmbvfb`
   └─> Push migrations: `supabase db push` (prefer CI: [`.github/workflows/supabase-deploy-prod.yml`](../../../.github/workflows/supabase-deploy-prod.yml))
   └─> Monitor production dashboard

CI/CD Integration

For automated deployments, use GitHub Actions with environment-specific secrets:
# .github/workflows/deploy-staging.yml
name: Deploy to Staging

on:
  push:
    branches: [staging]

jobs:
  deploy:
    runs-on: ubuntu-latest
    env:
      SUPABASE_ACCESS_TOKEN: ${{ secrets.SUPABASE_ACCESS_TOKEN }}
      SUPABASE_PROJECT_ID: ${{ secrets.STAGING_PROJECT_ID }}
      SUPABASE_DB_PASSWORD: ${{ secrets.STAGING_DB_PASSWORD }}
    
    steps:
      - uses: actions/checkout@v4
      - uses: supabase/setup-cli@v1
      - run: supabase link --project-ref $SUPABASE_PROJECT_ID
      - run: supabase db push

Best Practices

1. Always Test Locally First

  • Use supabase db reset to test migrations from scratch
  • Verify migrations are idempotent (can be run multiple times safely)

2. Staging Before Production

  • NEVER deploy directly to production
  • Always validate in staging first
  • Use staging for UAT sign-offs

3. Environment Isolation

  • Keep production and staging completely separate
  • Never use production data in staging
  • Use synthetic test data in staging

4. Configuration as Code

  • Keep config.toml in version control
  • Use [remotes] blocks for environment-specific settings
  • Document any manual configuration changes

5. Secrets Management

  • Never commit secrets to git
  • Use .env files for local development (gitignored)
  • Use supabase secrets set for remote environments
  • Rotate secrets regularly

6. Migration Safety

  • Always review migrations before deploying
  • Test rollback procedures
  • Use transactions where possible
  • Avoid breaking changes when possible

Troubleshooting

Issue: Wrong Project Linked

Symptom: supabase db push deploys to wrong environment Solution:
# Check current link
supabase status

# Unlink and re-link
supabase unlink
supabase link --project-ref <correct-project-id>

Issue: Config Not Applied

Symptom: Remote-specific config not taking effect Solution:
  • Verify project_id in [remotes.*] matches actual project ID
  • Use supabase config push to sync config to remote
  • Check that you’re linked to correct project

Issue: Secrets Not Available

Symptom: Edge functions can’t access secrets Solution:
# List secrets for current project
supabase secrets list

# Set missing secrets
supabase secrets set KEY=value

# Verify secrets are set
supabase secrets list

Migration from Current Setup

Current State

  • config.toml has single project_id = "<STAGING_PROJECT_REF>" (staging)
  • Manual switching between environments
  1. Backup current config:
    cp supabase/config.toml supabase/config.toml.backup
    
  2. Update config.toml:
    • Add default local configuration
    • Add [remotes.production] block
    • Add [remotes.staging] block
    • Keep current staging project_id
  3. Test local development:
    supabase start
    # Verify local stack works
    
  4. Test staging link:
    supabase link --project-ref <STAGING_PROJECT_REF>
    supabase status
    
  5. Test production link:
    supabase link --project-ref srcaoozjkrughebmbvfb
    supabase status
    
  6. Update documentation:
    • Update docs/development/ENVIRONMENT_CONFIG.md with new workflow
    • Document switching process for team

Database connections for scripts (pooler, not direct)

Supabase’s direct Postgres hostname (db.<project-ref>.supabase.co) is often IPv6-only. Many local networks and CI runners only resolve IPv4, which surfaces as ENOTFOUND or connection timeouts when using pg, psql, or pg_dump. Use the Session pooler for any script that opens a raw Postgres connection from a laptop or CI:
  • Dashboard → Project SettingsDatabaseConnection stringSession mode (Supavisor, port 5432).
  • URI shape: postgresql://postgres.<PROJECT_REF>:<PASSWORD>@aws-0-<REGION>.pooler.supabase.com:5432/postgres
This repo’s seed tooling reads:
  • SUPABASE_DEV_DB_URL — dev (zkgxozahyczcnzpwhbbf), pooler URI.
  • SUPABASE_PROD_DB_URL — prod (srcaoozjkrughebmbvfb), pooler URI.
Keep these in .env.local only (gitignored). See .env.local.example.

Applying the global system-defaults bundle to prod

  1. Regenerate supabase/seeds/system-defaults/00_global_system_defaults.sql (npm run seed:extract-system-defaults or --source rest).
  2. Prefer recording the load in migration history: in Cursor, use the Supabase MCP apply_migration tool against srcaoozjkrughebmbvfb with migration name seed_global_system_defaults_bundle and query set to the file contents (same SQL as the SQL Editor path).
  3. Fallback: paste the file into the prod project SQL Editor and run.
  4. Fresh production project (empty / first-time): run the manual GitHub Actions workflow Bootstrap Supabase (production) (see workflow table below). It runs supabase db push --include-all, then supabase db query --linked --file on 00_global_system_defaults.sql, then optional npm run check:prod-seed-parity when SUPABASE_PROD_DB_URL is configured as a secret.

Migration lanes (schema vs system data)

New migrations must follow the lane rules in MIGRATION_LANES.md so CI can block dev-only URLs, JWTs in SQL, and non-idempotent catalog inserts after the strict cutoff timestamp.

Ongoing parity check

npm run check:prod-seed-parity compares prod catalog row counts to dev (if SUPABASE_DEV_DB_URL is set) or to documented minimum baselines. It skips when SUPABASE_PROD_DB_URL is unset (e.g. CI without secrets). Set that variable in CI to enforce the gate in automation.

GitHub Actions (Supabase deploy + drift)

Workflows live under .github/workflows/:
WorkflowTriggerAction
supabase-deploy-prod.ymlPush to production touching supabase/migrations/**, supabase/functions/**, or supabase/config.tomlsupabase linksrcaoozjkrughebmbvfb → post-link migration audit → db push --yes → deploy changed Edge Functions only (or all if _shared / config / unknown before SHA)
supabase-bootstrap-prod.ymlManual workflow_dispatch with confirm bootstrap-new-proddb push --include-alldb query --linked system-defaults bundle → optional seed parity
supabase-deploy-dev.ymlPush to prod with same path filters (or manual)supabase linksoft drift check (auto-dispatches supabase-sync-from-dev.yml if drift exists; bypass via force_deploy_dev) → db push --yes → selective function deploy
supabase-sync-from-dev.ymlDaily 06:00 UTC + manual workflow_dispatch with migration_namesupabase db diff --linked -f <slug> → validates with db reset + RLS / FK / lane guards → regenerates src/integrations/supabase/types.ts → opens PR to dev with the captured migration
supabase-promote-pr.ymlPR → production when migrations (or lane script) changeMigration lane report + sticky PR comment
db-migration-guard.ymlPR → main or production when migrations changeRLS init-plan + FK index checks; migration lane on main PRs only
build.ymlPR → main, production, or feature/**Full app CI gate
Repository secrets (Actions → Secrets and variables → Actions):
SecretUsed by
SUPABASE_ACCESS_TOKENSupabase CLI auth (deploy, drift, bootstrap)
SUPABASE_PROD_DB_PASSWORDsupabase-deploy-prod.yml, supabase-bootstrap-prod.yml (supabase link --password)
SUPABASE_DEV_DB_PASSWORDsupabase-deploy-dev.yml, supabase-drift-check.yml
SUPABASE_PROD_DB_URL (optional)supabase-bootstrap-prod.yml seed parity step
db diff --linked requires Docker on the runner; if the drift job logs a CLI/Docker failure, fix runner setup or run the diff locally.

References


Summary

Recommended Approach: Use [remotes] configuration blocks in config.toml to define environment-specific settings. This provides: ✅ Clear separation between environments
✅ Environment-specific configuration overrides
✅ Version-controlled configuration
✅ Easy switching via supabase link
✅ Support for different settings per environment
Workflow:
  1. Local: supabase start (uses default config), then supabase db reset when testing migrations.
  2. Dev (Encore OS): zkgxozahyczcnzpwhbbf — primary schema changes via Lovable + MCP; optional GitHub prod branch workflow mirrors repo → Dev.
  3. Production (encore_os_prod): srcaoozjkrughebmbvfb — promote via GitHub production branch (CI) or manual supabase link + db push + functions deploy following SUPABASE_RECONCILE_2026-04.md.
This approach aligns with Supabase best practices and provides a scalable, maintainable multi-environment setup.