---
title: "How to migrate Bubble app to Supabase, users included"
description: "How to migrate Bubble app to Supabase: export past the 50,000-item cap, rebuild privacy rules as RLS, and move users on their next login without resets."
url: https://zeroic.in/blog/migrate-bubble-app-supabase
---

[The Zeroic Journal](https://zeroic.in/blog)

[No-code](https://zeroic.in/blog/topic/no-code) October 9, 2026 9 min read

# How to migrate Bubble app to Supabase without password resets

A step-by-step Bubble exit for the data layer: exports, Postgres tables, row level security, files, and moving every user without a reset email.

By Khushi Arora Partner @ Zeroic

On this page

1. [How to migrate Bubble app to Supabase, in order](#how-to-migrate-bubble-app-to-supabase-in-order)
2. [Export every data type past the 50,000 cap](#export-every-data-type-past-the-50000-cap)
3. [Rewrite privacy rules as row level security](#rewrite-privacy-rules-as-row-level-security)
4. [Move users on their next login](#move-users-on-their-next-login)
5. [Cut over with a delta sync](#cut-over-with-a-delta-sync)
6. [Which migration path fits your app](#which-migration-path-fits-your-app)
7. [What building Glibzter on Bubble in 6 weeks taught us](#what-building-glibzter-on-bubble-in-6-weeks-taught-us)

On this page 7 sections

1. [How to migrate Bubble app to Supabase, in order](#how-to-migrate-bubble-app-to-supabase-in-order)
2. [Export every data type past the 50,000 cap](#export-every-data-type-past-the-50000-cap)
3. [Rewrite privacy rules as row level security](#rewrite-privacy-rules-as-row-level-security)
4. [Move users on their next login](#move-users-on-their-next-login)
5. [Cut over with a delta sync](#cut-over-with-a-delta-sync)
6. [Which migration path fits your app](#which-migration-path-fits-your-app)
7. [What building Glibzter on Bubble in 6 weeks taught us](#what-building-glibzter-on-bubble-in-6-weeks-taught-us)

Here’s how to migrate Bubble app to Supabase without losing data or users: export each data type in Created Date windows to stay under the Data API’s 50,000-item cap, rebuild it as Postgres tables, rewrite privacy rules as row level security, then move each user on their next login. Done in that order, nobody gets a password reset email.

Most guides stop at the data. The data is the easy part. The parts that hurt are the users (Bubble won’t hand over password hashes), the privacy rules (they don’t translate one to one) and the files, which still live on Bubble’s storage after you’ve moved everything else. If you’re still deciding whether to leave at all, start with [the four signs a Bubble app has outgrown the platform](https://zeroic.in/blog/when-to-rebuild-your-bubble-app). This piece assumes you’ve decided.

## How to migrate Bubble app to Supabase, in order

The order matters more than any single step. Each one depends on the one before it.

1. **Inventory.** List every data type, field, option set, privacy rule, file field and workflow, including backend and scheduled workflows. This list is your test plan for cutover.
2. **Schema.** Build the Postgres tables in Supabase, with a `bubble_id` column on every table.
3. **Data.** Export from Bubble, load into Supabase, and rehearse it until the row counts match.
4. **Rules.** Rewrite each privacy rule as a row level security policy before any client touches the new database.
5. **Users.** Pre-create every account in Supabase Auth, then move passwords on first login.
6. **Files and cutover.** Copy files to Supabase Storage, run a final delta sync, and switch traffic.

Supabase gives you Postgres, auth, storage and edge functions. It doesn’t give you Bubble’s visual workflow editor, so the workflows in your inventory become database functions, edge functions or code in your new frontend. That’s the largest piece of work in any Bubble exit, and it’s separate from this guide.

## Export every data type past the 50,000 cap

Bubble gives you two ways out. The first is the editor: under Data, App data, you pick a data type, set up a view, click Export and choose CSV, JSON or NDJSON. [Bubble’s export docs](https://manual.bubble.io/help-guides/data/the-database/export-import-data/exporting-data) say it emails you a download link when the file is ready, and that the export shares a queue with your scheduled API workflows, so it can wait its turn on a busy app. One known issue to plan for: date interval fields export as text like “2 days” even though Bubble stores them as milliseconds. Convert those before loading.

The second way is the Data API, and it’s the one to script, because you’ll run the export more than once. You enable it under Settings, API, and choose which data types to expose. The catch is in [Bubble’s Data API request docs](https://manual.bubble.io/core-resources/api/the-bubble-api/the-data-api/data-api-requests): there is a limit of 50,000 items in any GET request, so with 100,000 records and the cursor at 50,001, you get nothing back. Enterprise plans raise that to 10,000,000.

The fix is to never ask for more than 50,000 at once. Filter each request with a constraint on Created Date, export one month (or one week, for busy tables) at a time, page through each window with the cursor, and write each window to its own NDJSON file. Turn on `exclude_remaining` for large tables so Bubble doesn’t count every remaining record on each call.

Keep three fields from every record:

- **`_id`** goes into a `bubble_id` text column with a unique index. Every reference between records in the export points at these IDs, so you’ll need them to rebuild foreign keys.
- **Created Date and Modified Date** go into `created_at` and `modified_at`. Bubble sets both itself and won’t let API clients override them, which makes Modified Date a reliable cursor for the delta sync at cutover.

### Map things, lists and option sets to Postgres

BubbleSupabase / Postgres

Data typeTable, `uuid` primary key plus `bubble_id`

Field pointing at another thingForeign key column, filled in a second pass from `bubble_id`

List of thingsJoin table with two foreign keys

List of text or numbersArray column, or a child table if you’ll filter on it

Option setEnum, or a lookup table

User data type`auth.users` plus a `profiles` table

Option sets need a decision of their own. [Bubble’s option set docs](https://manual.bubble.io/help-guides/data/static-data/option-sets) say they’re part of your app’s source code, stored as JSON in a file called `dynamic.js` that ships with the app. Your export carries the option value on each record and nothing about the set itself. Rebuild short, stable lists as enums. Rebuild anything with extra attributes (a label, a color, a sort order) as a lookup table.

To load the files, [Supabase’s import guide](https://supabase.com/docs/guides/database/import-data) lists the dashboard CSV importer, `COPY` through `psql`, and pgloader. The dashboard importer has a 100MB size limit, so anything bigger goes through `COPY`. The same guide recommends disabling triggers during the import and building indexes afterwards. Load all tables first, then run a second pass that turns `bubble_id` references into foreign keys.

## Rewrite privacy rules as row level security

Bubble’s privacy rules and Supabase’s row level security do the same job with opposite defaults, and that’s where migrations leak data.

In Bubble, the privacy editor marks each data type as either “Privacy rules applied” or “Publicly visible”, per [Bubble’s privacy rule docs](https://manual.bubble.io/help-guides/data/the-database/protecting-data-with-privacy-rules). Each rule grants permissions like finding records in searches, viewing files attached to a record, and viewing individual fields.

In Supabase, [the row level security guide](https://supabase.com/docs/guides/database/postgres/row-level-security) is blunt: a table in an exposed schema without RLS is readable and writable by any role with a grant on it. Once RLS is on, nothing is readable through the API with the publishable key until you write policies. So turn RLS on for every table before you load data, then add policies one rule at a time:

```
alter table public.orders enable row level security; create policy "Owners can read their orders" on public.orders for select to authenticated using ( (select auth.uid()) = owner_id );
```

Two traps. Bubble’s field-level permissions (hide the email field from everyone but the owner) have no direct RLS equivalent, because RLS filters rows, not columns. Move those fields into a separate table with a stricter policy, or serve them through a view or a server-side function. And `owner_id` has to hold the Supabase user’s UUID, which is why users are created before you write ownership columns (next section). When a policy doesn’t behave, our guide to [why Supabase row level security fails](https://zeroic.in/blog/supabase-row-level-security-not-working) covers the six causes we see most.

## Move users on their next login

This is the step that decides whether your launch day is quiet. Bubble stores passwords separately and encrypted, so they’re [not usable by another system](https://forum.bubble.io/t/exporting-user-data/37259), as a long-running Bubble forum thread explains. The common answer is to import emails and send everyone a reset link. Every user who ignores that email is locked out until they find it.

There’s a better route, built from two documented features:

1. **Bubble can still check a password.** An API workflow in your Bubble app that runs the Log the User in action takes an email and password, and [Bubble responds with a token if it succeeds](https://bubble.io/support/en/articles/10763669-authenticating-with-bubble-api-as-a-user).
2. **Supabase accepts a password you already have.** [Supabase’s Auth0 migration guide](https://supabase.com/docs/guides/platform/migrating-to-supabase/auth0) shows `auth.admin.createUser` taking a bcrypt or Argon2 `password_hash`, or a plaintext password it hashes for you, with `email_confirm: true` for confirmed users. And [`auth.admin.updateUserById`](https://supabase.com/docs/reference/javascript/auth-admin-updateuserbyid) can set a password on an existing user.

Put together:

- **Before cutover,** create every Bubble user in Supabase Auth with their email, `email_confirm: true`, no password, and their `bubble_id` in `app_metadata`. Now every user has a Supabase UUID, so you can fill every `owner_id` column during the data load.
- **On login in the new app,** try Supabase first. If that fails and the user is still flagged as unmigrated, your server sends the email and password to the Bubble login workflow. If Bubble returns a token, call `updateUserById` with that password, clear the flag and sign the user in. The user typed their usual password and never noticed.
- **After a few weeks,** send a normal reset email to the users who haven’t logged in, then switch off the Bubble login workflow.

Run the Bubble check on your server only, over HTTPS, rate-limited, and never log the password. Users who signed up with Google or another OAuth provider don’t need any of this; they sign in with the same provider on Supabase.

### Copy files before you cancel the plan

File fields in your export are URLs pointing at Bubble’s storage. Download each file, upload it to a Supabase Storage bucket, and rewrite the URL column to the new path. [Supabase’s upload docs](https://supabase.com/docs/guides/storage/uploads/standard-uploads) recommend standard uploads for files up to 6MB and resumable TUS uploads above that. Run the file copy early, check the counts, and keep your Bubble plan active until every URL in the database points at Supabase.

## Cut over with a delta sync

Rehearse the full export and import at least twice, on a staging Supabase project, comparing row counts per table. Then on cutover day, run only the delta: every record whose Modified Date is newer than your last full export. Freeze writes on the Bubble side for the minutes that takes, switch traffic, and keep Bubble read-only for a few weeks as your fallback. Keep the URL structure identical too; the [Bubble rebuild guide](https://zeroic.in/blog/when-to-rebuild-your-bubble-app) covers why changed URLs cost you rankings.

If you’re moving the app one piece at a time, the same scripts become a one-way sync: Bubble stays the source of truth, Supabase is read-only for the new code, and write-back comes later.

## Which migration path fits your app

Your appPath

Under 50,000 records per type, few filesEditor CSV export, dashboard import, one weekend cutover

Any type over 50,000 recordsScripted Data API export in Created Date windows, `COPY` import

Users who would churn on a reset emailPre-create users, migrate passwords on login

Mostly Google or OAuth sign-inImport users, skip the password step

Heavy field-level privacy rulesSplit sensitive fields into their own tables before loading

Frontend still on Bubble for monthsOne-way sync, move one feature at a time

If you’re not sure which row you’re in, an hour with your Bubble data tab and privacy editor will tell you. Count records per type, count file fields, count privacy rules with field-level permissions.

## What building Glibzter on Bubble in 6 weeks taught us

We built [Glibzter on Bubble in 6 weeks](https://zeroic.in/glibzter): scored assessments, a video course player, 1:1 coach booking and Stripe checkout. It runs two product surfaces on one auth system, one wallet and one content backbone, all in a single Bubble app. That design is what makes a Bubble app feel like one product, and it’s also why exits get hard: one User data type, referenced by assessments, bookings, payments and course progress, with privacy rules on each.

Building that taught us where the export has to be careful. Every reference in the export points at a `_id`, so the user table has to be mapped first or nothing joins. Option sets live outside the database, so a migration that only reads the export misses the lists your app runs on. And the privacy rules on a shared User type are the ones most likely to leak when translated in a hurry. We plan every [Bubble to code migration](https://zeroic.in/bubble-to-code) around those three points.

That work runs as our [Code Rescue engagement](https://zeroic.in/services/code-rescue): a 2-4 week block, starting at $6,000, that moves one piece of a Bubble app, usually the backend and data, into code while the rest keeps running. If you’d rather know what’s hurting before you move anything, a [Performance or Security audit](https://zeroic.in/services/ai-audit) starts at $1,000 and takes 1-2 weeks.

## Frequently asked questions

**Can I export user passwords from Bubble?**

No. Bubble stores passwords separately and encrypted, so its exports carry no hash another system can reuse. You can still avoid a mass reset: pre-create every account in Supabase, then on each user's first login check the password against Bubble's Log the User in action and, if it passes, set that same password in Supabase through the admin API.

**How do I export more than 50,000 records from Bubble?**

Split the export into date windows. Bubble's Data API stops returning results past 50,000 items in any GET request on non-Enterprise plans, so filter each request by Created Date, page through each window with the cursor, and write the results to NDJSON. For one-off exports, the App data tab can also export a data type to CSV, JSON or NDJSON.

**What happens to Bubble option sets in Supabase?**

They become Postgres enums or small lookup tables. Option sets live in your Bubble app's source code instead of its database, so a data export only gives you the option values stored on each record. Use an enum when the list rarely changes, and a lookup table when it carries extra attributes like a label, color or sort order.

**How long does a Bubble to Supabase migration take?**

For the data layer alone, plan on a few weeks for a mid-sized app: schema and export scripts first, then row level security and user migration, then a rehearsal run and the cutover. Rebuilding the frontend and workflows is the larger job, which is why we move a Bubble app in 2-4 week blocks instead of one big rewrite.

**Do I need to rebuild my Bubble workflows in Supabase?**

Yes. Supabase gives you Postgres, auth, storage and edge functions, but it has no visual workflow editor, so each Bubble workflow becomes database logic, an edge function or code in your new app. Inventory every workflow before the move, including backend and scheduled ones, because the forgotten ones are the ones that break after cutover.

## References

1. [Bubble - Exporting data](https://manual.bubble.io/help-guides/data/the-database/export-import-data/exporting-data)
2. [Bubble - Data API requests](https://manual.bubble.io/core-resources/api/the-bubble-api/the-data-api/data-api-requests)
3. [Bubble - The Data API](https://manual.bubble.io/core-resources/api/the-bubble-api/the-data-api)
4. [Bubble - Option sets](https://manual.bubble.io/help-guides/data/static-data/option-sets)
5. [Bubble - Protecting data with privacy rules](https://manual.bubble.io/help-guides/data/the-database/protecting-data-with-privacy-rules)
6. [Bubble - Authenticating with the Bubble API as a user](https://bubble.io/support/en/articles/10763669-authenticating-with-bubble-api-as-a-user)
7. [Bubble forum - Exporting user data](https://forum.bubble.io/t/exporting-user-data/37259)
8. [Supabase - Import data into Supabase](https://supabase.com/docs/guides/database/import-data)
9. [Supabase - Row Level Security](https://supabase.com/docs/guides/database/postgres/row-level-security)
10. [Supabase - Migrate from Auth0 to Supabase Auth](https://supabase.com/docs/guides/platform/migrating-to-supabase/auth0)
11. [Supabase - auth.admin.updateUserById](https://supabase.com/docs/reference/javascript/auth-admin-updateuserbyid)
12. [Supabase - Standard uploads](https://supabase.com/docs/guides/storage/uploads/standard-uploads)

#bubble#supabase#migration#postgres#no-code

More from the journal

[ No-code

## [When to rebuild your Bubble app in code - and when to stay](https://zeroic.in/blog/when-to-rebuild-your-bubble-app)

The four signs a Bubble app has outgrown the platform, and how to move to code one piece at a time without freezing the product.

Khushi Arora Sep 21, 2026 · 8 min read

[ No-code

## [Bubble vs Webflow vs custom code: which fits your SaaS MVP (2026)](https://zeroic.in/blog/bubble-vs-webflow-vs-custom-code)

Webflow, Bubble and custom code solve three different problems. Here's which one your first version needs, and the combination most SaaS teams end up running.

Khushi Arora Sep 30, 2026 · 6 min read

[ SaaS

## [Supabase vs Firebase for SaaS: cost at 1k, 10k and 100k users](https://zeroic.in/blog/supabase-vs-firebase-cost)

Firebase is cheaper while you're small. Past 10,000 users, per-read billing and auth fees flip it, and leaving Firestore costs far more than leaving Postgres.

Prashant Abbi Oct 13, 2026 · 6 min read

## Hear from Khushi within 24 hours.

**Khushi Arora** Partner @ Zeroic

Planning a Bubble exit and worried about your users? We move Bubble apps to code one piece at a time - data, privacy rules and logins included - in fixed 2-4 week blocks while the app keeps running. Tell us what yours does.

[Plan a Bubble migration](https://zeroic.in/bubble-to-code), or [book a 30-minute call](https://zeroic.in/book)

Pages [Services](https://zeroic.in/services) [Work](https://zeroic.in/work) [Blog](https://zeroic.in/blog) [About](https://zeroic.in/about) [Guide for AI](https://zeroic.in/llms.txt)

Services [Audits](https://zeroic.in/services/ai-audit) [MVP Sprint](https://zeroic.in/services/mvp-development) [Retainer](https://zeroic.in/services/senior-developers) [Code Rescue](https://zeroic.in/services/code-rescue) [Bubble to code](https://zeroic.in/bubble-to-code) [AI development](https://zeroic.in/ai-development-company)

Industries [Marketplaces](https://zeroic.in/marketplace-app-development) [B2B SaaS](https://zeroic.in/saas-mvp-development) [FinTech](https://zeroic.in/fintech-app-development) [Telehealth](https://zeroic.in/telehealth-app-development) [EdTech](https://zeroic.in/edtech-app-development) [All industries →](https://zeroic.in/industries)

Developers [Bubble](https://zeroic.in/hire-bubble-developers) [React](https://zeroic.in/hire-nextjs-developers) [Python](https://zeroic.in/hire-python-developers) [AI & LLMs](https://zeroic.in/hire-ai-developers) [API integrations](https://zeroic.in/api-integration-services) [All developers →](https://zeroic.in/developers)

Contact [Email](mailto:zeroic.in@gmail.com) [WhatsApp](https://wa.me/919873927225) [LinkedIn](https://www.linkedin.com/company/zeroic/) [X (Twitter)](https://x.com/PrashantAbbi) [Book a call](https://zeroic.in/book)

© 2026 Zeroic [Privacy](https://zeroic.in/privacy) [Terms](https://zeroic.in/terms)

Built in India · Shipping globally

SINCE
