Supabase Staging Environment: Complete Setup Guide
Published Jul 5, 2026 · 9 min read
A Supabase staging environment is a separate Supabase project that mirrors your production project's structure — schema, RLS policies, functions, auth configuration — but contains no real user data. You set one up by creating a fresh project, replaying your production structure into it (via migrations, supabase db dump, or a tool like SupaClone), and then adjusting the handful of settings that must differ: environment variables, redirect URLs, and secrets. This guide walks through the full setup, including what it costs, and what you should deliberately not copy.
Why do you need a separate staging project?
Because testing against production is how outages happen. A migration that locks a table, an RLS policy change that silently hides rows, a trigger that misfires — you want to discover these where the blast radius is zero. Supabase itself recommends promoting changes through separate environments instead of editing production directly (Managing Environments).
The practical question is cost:
- Free plan: you get a limit of 2 active projects, so a prod + staging pair fits — barely. The catch: free projects are paused after 1 week of inactivity (pricing), so an idle staging project will be asleep exactly when you need it, and unpausing takes a few minutes. Workable for side projects, annoying for teams.
- Pro plan ($25/month): includes $10/month in compute credits covering one Micro instance. A second project (your staging) runs its own compute, so it adds roughly $10/month.
- Branching: available on Pro and Team plans at $0.01344 per branch per hour — about $9.80/month if you keep a persistent branch running continuously. Not available on the free plan.
So on Pro, a persistent branch and a separate staging project cost nearly the same. The difference is behavioral, which brings us to the next question.
Should you use branching instead of a separate project?
Branching is the better fit for short-lived preview environments — each pull request gets its own isolated database that is deleted when the PR merges. Supabase also supports persistent branches explicitly recommended for staging and QA (branching docs).
A separate project is the better fit when you want staging to be a first-class citizen: its own dashboard, stable API keys and URL, its own auth configuration, no coupling to a GitHub integration, and no dependency on clean migration history (branches are built by replaying your migrations — if production has drifted from your migration files, the branch won't match production). Branches also start empty by design — "New branches do not start with any data from your main project," per the docs — so branching doesn't solve the schema-sync problem either.
We compare both approaches in depth in Supabase branching vs separate projects. The short version: previews → branching, long-lived staging → separate project. The rest of this guide assumes a separate project.
How do you create the staging project?
- In the Supabase dashboard, create a new project — name it something unambiguous like
myapp-staging. Put it in the same region as production so latency-sensitive behavior (triggers, function timing) is comparable. - Use a strong, different database password than production.
- Leave it completely empty. Don't click around in the Table Editor "just to set things up" — every method of copying structure below assumes a fresh target, and Supabase's own environment guide insists on a new project for staging because a previously modified project causes migration tooling to misbehave.
How do you get your production schema into staging?
This is the actual work, and the right method depends on the state of your project.
Option 1: Replay your migrations (greenfield projects)
If your production schema was built entirely from checked-in migration files, you're in the ideal case. Link the CLI to staging and push:
supabase link --project-ref <staging-project-ref>
supabase db push
Supabase's recommended team setup automates this with GitHub Actions: pushes to develop run supabase db push against staging, merges to main push to production (Managing Environments). We cover that pipeline end-to-end in the dev/staging/prod workflow guide.
The honest caveat: this only works if migrations are your complete source of truth. Most real projects have at least some drift — a policy edited in the dashboard, an index added during an incident.
Option 2: Dump and restore with the CLI (brownfield projects)
If you built your schema in the dashboard (or migrations have drifted), dump the real production schema:
supabase db dump --db-url "$PROD_DB_URL" -f schema.sql
psql "$STAGING_DB_URL" -f schema.sql
supabase db dump runs pg_dump in a container with flags that exclude Supabase-managed schemas (auth, storage, extension-created schemas), and by default dumps neither data nor roles (CLI reference) — which is exactly what you want for staging. Alternatively, supabase db pull generates a migration file from the remote schema, giving you a starting point for Option 1 going forward; note it excludes the auth and storage schemas unless you pass --schema auth,storage (CLI reference).
The failure modes live in the details: ownership errors on restore, objects that reference managed schemas, storage bucket policies that don't come along, and no verification that what landed in staging matches production. We've documented these in pg_dump and pg_restore with Supabase: the pitfalls.
Option 3: SupaClone (brownfield, without the pitfalls)
SupaClone automates exactly this path: it clones your production project's structure — schemas, tables, RLS policies, functions, triggers, indexes, views, enums, extensions — into a fresh, empty target project using native pg_dump/pg_restore with a baseline-aware plan that correctly skips Supabase-managed schemas. Optional add-ons bring over Storage buckets, Edge Functions, and Auth configuration. You connect both projects via Supabase OAuth — no connection strings to copy around — and every run ends with a field-by-field verification and a report of what was cloned, skipped, or needs a manual step. The full walkthrough is in how to clone a Supabase project.
To be clear about fit: SupaClone clones structure, not data, and the target must be a fresh, empty project. If you have pristine migration history, Option 1 is free and correct — use it.
Migrations (db push) | CLI dump + psql | SupaClone | |
|---|---|---|---|
| Works without migration history | No | Yes | Yes |
| Skips managed schemas correctly | n/a | Yes (db dump) | Yes (baseline-aware) |
| RLS policies included | If in migrations | Yes | Yes |
| Storage buckets / Edge Functions | No | No | Optional add-ons |
| Auth config | No | No | Optional add-on (secrets excluded) |
| Verification that staging matches prod | No | No | Field-by-field report |
| Cost | Free | Free | From $7/month |
What must be configured differently in staging?
Copying structure is half the job. These settings must diverge between the two projects:
- App environment variables: staging gets its own
SUPABASE_URLand API keys. Wire them into your hosting provider's staging environment (e.g. Vercel preview/staging env vars) — never let a staging deploy read production keys. - Site URL and redirect URLs: in Authentication → URL Configuration, the Site URL is the default redirect when no
redirectTois passed, and anyredirectTonot on the allow list is rejected (redirect URLs docs). Set staging's Site URL to your staging domain and add its callback paths. Wildcards (*,**) are supported for preview URLs, but Supabase recommends exact URLs in production. - OAuth providers: each provider (Google, GitHub, …) needs the staging project's callback URL registered. Cleanest approach: a separate OAuth app per environment, so staging logins never show up in production OAuth metrics.
- SMTP / email: either point staging at a sandbox mailer (Mailtrap, Resend test mode) or accept Supabase's heavily rate-limited built-in sender. You do not want staging accidentally emailing real users.
- Webhooks and Edge Function secrets: re-enter these by hand with staging-appropriate values (test-mode Stripe keys, staging endpoints). Secrets are never portable between projects — and shouldn't be.
How do you keep staging in sync over time?
Staging environments rot. Three months in, production has two new tables and a rewritten set of policies, and staging tests pass against a schema that no longer exists. Pick a sync strategy on day one:
- Migrations-first (best long-term): after the initial setup, enforce that every schema change is a migration file, deployed to staging before production via CI. Drift can't accumulate because the dashboard is read-only by convention.
- Periodic re-clone: if dashboard-driven changes are a reality for your team, periodically delete and recreate staging from production. With SupaClone that's a repeatable clone into a fresh project with a verification report each time; RLS policies — the thing most often edited ad hoc — come along, which is otherwise its own manual chore (see copying RLS policies between projects). Scheduled clones are on SupaClone's roadmap, not shipped yet — today a re-clone is a manual trigger.
- Diff-and-patch: run
supabase db pullagainst production periodically and apply the generated migration to staging. Cheap, but auth/storage schema changes and non-schema config (buckets, auth settings) escape it.
Whichever you choose, make staging deploys precede production deploys in CI — staging that gets changes after production is a changelog, not a safety net.
What should you never copy to staging?
- Secrets. Service-role keys, JWT secrets, SMTP credentials, OAuth client secrets, Edge Function secrets. Staging must have its own. This is why SupaClone excludes secrets by design and lists them as manual steps in the clone report instead — a copied production secret in a lower-security environment is a breach waiting to happen.
- User data. Copying
auth.usersand user-generated rows into staging means production personal data now lives in an environment with weaker access controls, more people holding keys, and no legal basis for processing. Under GDPR that's a real problem, not a theoretical one: staging data is still personal data. The safe pattern is structure-only staging plus seed scripts with synthetic users (supabase db dump --data-onlyexists, but point it at seed data, not production). Anonymized data clones are coming to SupaClone precisely because "realistic but not real" is what staging actually needs — until then, seed scripts are the answer. - Production analytics/webhook wiring. Anything that makes staging activity show up in production dashboards or trigger real side effects (emails, charges, notifications).
If you want to try the structure-only clone path, SupaClone's 14-day free trial includes one successful clone — enough to stand up a complete staging environment and read the verification report before paying anything.
FAQ
Can I run a Supabase staging environment on the free plan?
Yes — the free plan allows 2 active projects, so prod + staging fits. The constraint is that free projects pause after 1 week of inactivity, so an idle staging project will need manual unpausing before use.
How much does a Supabase staging environment cost on the Pro plan?
Roughly $10/month: the Pro plan's $10 compute credit covers your production Micro instance, and the staging project adds its own Micro compute at about $10/month. A persistent branch costs nearly the same (~$9.80/month at $0.01344/hour).
Should staging have the same region as production?
Yes, ideally. Same region keeps latency, trigger timing, and extension availability comparable, so staging behavior predicts production behavior more reliably.
Does supabase db dump copy my auth users to staging?
No. supabase db dump excludes Supabase-managed schemas (including auth) and dumps no data by default — you'd have to explicitly pass --data-only to get data. For staging, that default is the correct behavior.
Can I copy production data into staging for realistic testing?
You can, but you usually shouldn't: production user data in staging creates GDPR exposure and widens your attack surface. Use synthetic seed data instead. Anonymized data cloning is on SupaClone's roadmap but not available yet.