Supabase branching vs. separate staging projects
Published Jul 5, 2026 · Updated Sep 15, 2026 · 10 min read
Use Supabase branching for short-lived preview environments - PR reviews, migration testing in CI, quick experiments - and use a separate Supabase project for a long-lived staging environment. Branching requires a paid plan and bills $0.01344 per branch per hour, which makes an always-on branch cost about as much as a dedicated project, without the isolation benefits. A separate project gives you a persistent, independently billed environment; the trade-off is that you have to get your production structure into it yourself.
Both options are legitimate. This post walks through what branching actually does in 2026 (it changed with Branching 2.0), what it costs, where it clearly wins, why branches drift from production, and why a plain pg_dump into a separate project leaves half the setup behind.
What is Supabase branching and how does it work?
Supabase branching creates isolated copies of your project - each branch gets its own database instance, API endpoints, Auth settings, Storage buckets, and Edge Functions. Changes in one branch don't affect any other branch or production.
There are two kinds of branches:
- Preview branches are ephemeral. In the Git workflow, opening a pull request spins one up automatically; it runs the migrations in
./supabase/migrations, applies new migrations on each push, and is paused after inactivity or deleted when the PR is merged or closed. - Persistent branches are long-lived. Supabase recommends them for staging, QA, or development environments. They are not auto-paused or auto-deleted.
Since Branching 2.0, Git is no longer required: you can create branches directly from the dashboard, the CLI, or the Management API - with Git, without Git, or both.
Two properties matter for the staging question:
- Branches start without data by default. From the branching docs: new branches don't start with any data or storage objects by default. You can seed a branch with
./supabase/seed.sql, but the seed runs exactly once, at branch creation. Dashboard branches can copy production data with an "Include data" option, but only with the Point-in-Time Recovery add-on, and the branch then has to be treated like production. - Branches are built from your migration history, not from the live production schema. That holds for Git-based branches and, once your project has run any migration, for dashboard branches too: a full schema dump is only used when the project has never run migrations. The next section covers what that means in practice.
How much does Supabase branching cost?
Branching is available on the Pro plan and above - the Free plan doesn't include it. Per the Supabase pricing page and the branching usage docs:
- Each branch bills $0.01344 per hour on the default Micro compute size.
- Billing is purely usage-based: there is no fixed fee, and you pay for the hours a branch runs.
- Compute Credits do not apply to Branching Compute. The $10/month credit that offsets your main project's compute does not offset branches.
- Branch disk usage counts toward your plan's included 8 GB; beyond that, disk is billed like any other database.
The math that decides the staging question: a short-lived preview branch that runs 30 hours during a PR costs about $0.40. A persistent branch running 24/7 costs $0.01344 × 720 ≈ $9.70 per month - roughly the same as the Micro compute for an additional dedicated project, except credits can't reduce it. Branching is priced for ephemeral use. The moment a branch becomes permanent, its cost advantage disappears.
What is Supabase branching great at?
Being fair here matters, because branching genuinely wins several workflows:
- PR preview environments. Open a PR, get a full isolated Supabase stack with its own credentials, wired into Vercel or your CI. Nothing else gives you this with so little setup.
- Migration testing in CI. Every push replays new migrations against the branch. If a migration fails, you find out in the PR - not during a production deploy. If you manage migrations in Git, this alone justifies branching.
- Cheap, disposable experiments. Cents per day, one click to create since Branching 2.0, zero cleanup guilt. Testing a risky schema refactor against a throwaway environment is exactly what per-hour billing is for.
- Merge-to-promote workflows. Merging a branch can promote its migrations to production, keeping your database changes on the same rails as your code changes.
If your team is on the Pro plan, keeps its schema in migration files, and wants per-PR environments, use branching. A separate project is the wrong tool for that job - provisioning and tearing down projects per PR is slow and manual.
Why don't Supabase branches match production?
Because a branch is only as complete as your migration history. Supabase builds it by replaying the migrations your project has run, and anything that never became a migration file is invisible to that process:
- a table created in the Table Editor during a demo,
- an RLS policy hand-edited in the dashboard after a security review,
- an index added in the SQL Editor during an incident and never backported.
Most projects older than a few months have some drift. It shows up in two ways:
- The branch builds, but is incomplete. Every migration applies, the branch reports healthy, and the objects created outside migrations aren't there. You notice days later.
- The branch fails. If a migration no longer fits the real schema, the replay stops partway and the branch sits in
MIGRATIONS_FAILED, empty or half-built. Supabase's documented fix is to read the workflow logs, correct the migration file, mark it applied withsupabase migration repair, and recreate the branch - sometimes more than once.
Even a perfect history misses custom roles created in the dashboard, which are not captured on branch creation. For repair versus re-baseline, see fixing a broken migration history; what branches miss goes deeper on each gap.
What are the limits of Supabase branching?
Beyond drift, the constraints that push teams toward a separate project:
- Paid plan required. No branching on the Free plan. If you're a solo dev or a small team on free-tier projects, branching isn't even on the menu.
- Per-hour billing punishes persistence. Staging is always-on, so a persistent branch costs dedicated-project money while still living inside your production project's org and lifecycle.
- No production data by default. Copying data requires the PITR add-on; otherwise you maintain seed files that run once, at branch creation.
- Same org, same blast radius. A branch is a child of your production project. If you want staging in a different organization (separate billing, separate access control, contractors who should never see prod), branching can't do that.
When is a separate staging project the better fit?
A dedicated Supabase project wins when the environment is long-lived and organizationally distinct:
- Persistent staging/QA with stable URLs and keys your team and CI reference for months. See setting up a Supabase staging environment.
- Cost control and Free-tier teams. A staging project can live in a Free-plan org at $0 (free projects pause after a week of inactivity; $10/month on Pro keeps it always-on). A persistent branch can never be free.
- Org and access separation. Different organization, members, and billing entity. Staging credentials that can't touch prod even in principle.
- No migration-history dependency. A separate project doesn't care whether your migration files match production, which is exactly the problem from the previous section.
Most teams running a dev → staging → prod workflow land on a hybrid: preview branches for PRs, plus one separate long-lived staging project.
What does pg_dump not copy from a Supabase project?
A separate project starts empty, and the obvious way to fill it is pg_dump or supabase db dump. That gets your database across, after the usual fight with Supabase-managed schemas (see the pg_dump and pg_restore guide). But a Supabase project is more than its database, and a dump copies only the database:
- Auth settings. Site URL, redirect URLs, enabled OAuth providers, email templates, and SMTP settings are project configuration managed through the Management API, not tables. The new project starts with defaults, and sign-in flows break until you re-enter everything.
- Storage files. At most the dump contains bucket metadata. The files themselves have to be copied through the Storage API, per Supabase's migration guide.
- Edge Functions. Not part of the database. They have to be downloaded and redeployed, and import maps or
deno.jsonfiles are not included in the download. - Secrets. Edge Function secrets are set per project and never leave it. Restored Vault data is encrypted with the old project's root key and stays unreadable until that key is copied over.
- Order-dependent setup. Database Webhooks and non-default extensions need to be enabled in the new project before the restore, and Realtime publications re-enabled after it.
Even Supabase's own "Restore to a new project" (paid plans with physical backups) lists Storage, Edge Functions, and Auth settings as manual reconfiguration. Most of these gaps don't raise an error during the restore. It finishes, and you discover the missing pieces when a login redirect fails or a function returns 404.
Supabase branching vs. separate project: comparison
| Branching | Project + SupaClone | Project + pg_dump | |
|---|---|---|---|
| Best for | PR previews, CI | Long-lived staging | One-off copies |
| Plan | Pro or above | Any, incl. Free | Any, incl. Free |
| Always-on cost | ~$9.70/mo per branch | $0-10/mo + from $9.99 once | $0-10/mo |
| Schema source | Migration history | Live database | Live database |
| Dashboard drift | Missing | Included | Included |
| Table data | None by default | Optional | Optional, manual |
| Auth settings | Project config | Copied, minus secrets | Not copied |
| Storage files | None by default | Selectable | Not copied |
| Edge Functions | From main | Selectable | Manual redeploy |
| Verification | Deploy status | Field-by-field report | None |
| Separate org | No | Yes | Yes |
How do you get production structure into a separate staging project?
The honest weakness of the separate-project approach is setup: recreating the schema is only the first step, and the pieces from the previous section are where most teams stall.
SupaClone automates this step. It clones your project's own schemas - tables, RLS policies, functions, triggers, indexes, views, enums, extensions - and, optionally, their data into a fresh, empty target, using native pg_dump/pg_restore with a baseline-aware plan that skips the Supabase-managed schemas. Because it reads the live database, dashboard-made changes come along too, so migration drift doesn't matter. Auth settings (redirect URLs, email templates, enabled providers and their client IDs), Storage buckets and files, and Edge Functions are native, selectable options. Secrets are never copied; provider secrets and the SMTP password show up as named manual steps in the report. Every run ends with a field-by-field verification report. A Single Run is $9.99 one-time and the 5-Pack is $29.99 one-time ($6.00 per run); Lifetime is $79.99 one-time with unlimited runs.
Note the scope honestly: SupaClone skips the Supabase-managed auth schema, so auth.users never comes across, and anonymized data cloning is coming soon. If you need a full copy including auth users and you're on a paid plan, Supabase's "Restore to a new project" is the tool for that.
Which should you use?
- Per-PR environments and CI migration testing: branching, no contest.
- Always-on staging on a budget or on the Free plan: separate project.
- Staging that must match live production, including drift, Auth settings, Storage, and Edge Functions: separate project, cloned from prod - the full walkthrough is here.
- Org/billing/access separation: separate project.
- Both needs: run both. Preview branches for PRs, one separate staging project for release testing.
FAQ
Is Supabase branching free?
No. Branching requires the Pro plan or above and bills $0.01344 per branch per hour on the default Micro compute size. Compute Credits do not apply to branching compute, so branches always add to your bill.
Do Supabase branches copy production data?
Not by default. New branches start without data or storage objects to protect sensitive production data. Dashboard branches can include production data if your project has the Point-in-Time Recovery add-on; otherwise you seed with supabase/seed.sql, which runs only once, when the branch is created.
Why is my Supabase branch stuck in MIGRATIONS_FAILED?
A migration in your history failed to apply on the fresh branch database, usually because the history no longer matches the real schema. Check the branch workflow logs, fix the migration file, mark it applied with supabase migration repair, and recreate the branch.
Does pg_dump copy Supabase Auth settings, Storage files, and Edge Functions?
No. A dump contains the database only. Auth settings such as redirect URLs and OAuth providers, Storage files, Edge Functions, and their secrets have to be migrated separately or re-entered by hand in the new project.
Does branching still require GitHub?
Not anymore. Since Branching 2.0 you can create and manage branches from the dashboard, the Supabase CLI, or the Management API - with or without Git. The automatic PR-preview workflow still uses the GitHub integration.
Can I use a persistent branch as my staging environment?
You can - Supabase recommends persistent branches for staging and QA. But it costs roughly $9.70/month uncovered by credits, lives inside your production org, and only contains what your migration history describes.
How do I make my separate staging project match production?
Either replay a complete, drift-free migration history and reconfigure Auth, Storage, and Edge Functions by hand, or clone the live project. SupaClone copies schema, optional data, Auth settings, Storage, and Edge Functions into a fresh empty project and verifies every field afterward.