Move from Supabase
Tested with: zb migrate 0.1 · PostgreSQL 17 · supabase-js 2
A Supabase project is a Postgres database plus the services around it. This guide moves the database first, with its data and row-level security policies, then points your application at the Databasezy project endpoint. Read the compatibility notes for the differences that remain.
What moves today, and what follows
Section titled “What moves today, and what follows”| Service | Capability | Status |
|---|---|---|
| Auth | Email and password sign-up and sign-in | Live |
| Auth | Magic links and email one-time codes | Live |
| Auth | Sessions with refresh-token rotation and reuse detection | Live |
| Auth | CAPTCHA with hCaptcha or Cloudflare Turnstile | Live |
| Auth | Password rules and leaked-password protection | Live |
| Auth | Rate limits and brute-force protection | Live |
| Auth | OAuth and social sign-in providers | Coming soon |
| Auth | Multi-factor authentication | Coming soon |
| Auth | SAML 2.0 single sign-on for your users | Coming soon |
| Auth | Third-party auth (Clerk, Auth0, Firebase, Cognito, WorkOS) | Coming soon |
| Auth | Anonymous sign-in and phone codes | Coming soon |
| Auth | User management in the portal | Coming soon |
| Auth | Import users from Supabase with their password hashes and ids | Coming soon |
| Auth | Server-side auth helpers (@supabase/ssr cookies, PKCE) | Coming soon |
| Auth | OAuth 2.1 / OpenID Connect provider: sign in with your app, MCP clients | Coming soon |
| Data API | REST over your tables (PostgREST) | Live |
| Data API | GraphQL (pg_graphql) | Live |
| Data API | SQL over HTTPS for every engine (/query/v1) | Live |
| Data API | TypeScript types for supabase-js (zb gen types) | Live |
| Storage | Buckets with row-level security and signed URLs | Coming soon |
| Storage | Resumable and multipart uploads | Coming soon |
| Storage | S3-compatible endpoint | Coming soon |
| Storage | Image transformations | Coming soon |
| Functions | TypeScript functions over HTTP, with secrets and logs | Coming soon |
| Functions | Cron, queue and database-change triggers | Coming soon |
| Realtime | Broadcast | Coming soon |
| Realtime | Presence | Coming soon |
| Realtime | Postgres changes filtered by row-level security | Coming soon |
If your application signs users in with Supabase Auth, plan the move around the user import: it brings users,
identities and password hashes across with the same user ids, so auth.uid() keeps matching the owner columns in
your tables and nobody has to reset a password. You can move and test the database and the Data API before it ships.
Move the database
Section titled “Move the database”-
Create the project and its primary database. Use the same Postgres major version as your Supabase project (Project settings → Infrastructure in Supabase):
shell zb projects create --name shopzb instances create --engine postgres --engine-version 17 --size s1 --region us-east --name shop-db --waitNote the instance id it prints (
inst_...), then chooseshop-dbas the primary database under Project settings. -
Turn on the extensions your schema uses (for example
vector,pg_cron,pgmq,pg_trgm) in the extensions manager, so the restore can create objects that depend on them. -
Copy with continuous sync. The migration CLI has a Supabase preset that rewrites pooler URLs, skips the platform schemas (
auth,storage,realtime,vault,graphql,cron, …) and restores your own schemas with their row-level security policies:shell zb migrate --from "postgresql://postgres.<ref>:<password>@aws-0-us-east-1.pooler.supabase.com:5432/postgres" \--to inst_01jb... --mode copy_and_sync --dry-run # preflight checklist, changes nothingzb migrate --from "postgresql://postgres.<ref>:<password>@aws-0-us-east-1.pooler.supabase.com:5432/postgres" \--to inst_01jb... --mode copy_and_syncDetails, troubleshooting and rollback: Migrate from Supabase.
-
Recreate scheduled jobs. pg_cron jobs live in the skipped
cronschema. Add them again in Cron or withselect cron.schedule(...).
Point your application at Databasezy
Section titled “Point your application at Databasezy”-
Turn on the Data API with the schemas your app uses (usually
public) under Platform → Data API. -
Create keys and keep the secret key on servers only:
shell zb projects keys create --name web --kind publishablezb projects keys create --name server --kind secret -
Regenerate types from the new project:
shell zb gen types --output src/lib/database.types.ts -
Swap the URL and keys. Client code stays the same:
src/lib/db.ts const supabase = createClient("https://<ref>.supabase.co", process.env.SUPABASE_ANON_KEY);const supabase = createClient("https://<ref>.us-east.databasezy.com:8443", process.env.DATABASEZY_PUBLISHABLE_KEY);Code that verifies tokens with Supabase’s JWT secret must switch to the project’s JWKS.
-
Cut over when
zb migrate statusreportsready_for_cutover: stop writes on Supabase, runzb migrate cutover <mig_id>, deploy the new configuration.
Users, storage and functions
Section titled “Users, storage and functions”zb migrate supabase imports what lives around the database. Run it before the database copy above: tables with
foreign keys to auth.users restore only once the users exist.
-
Turn on Auth for the project (Platform → Auth) and, for files, Storage.
-
Set the Supabase credentials as environment variables on your machine. They are used there for this run only, never sent to Databasezy and never printed:
shell export SUPABASE_DB_URL='postgresql://postgres:<password>@db.<ref>.supabase.co:5432/postgres' # direct connectionexport SUPABASE_SERVICE_ROLE_KEY='<service_role key>' # Project settings → APIexport SUPABASE_ACCESS_TOKEN='sbp_...' # optional: lists deployed functions and secret names -
Dry run - counts users and their password hash algorithms, buckets and files, functions and secret names, and lists row-level security policies that call Supabase-only functions:
shell zb migrate supabase --supabase-url https://<ref>.supabase.co --dry-run -
Import:
shell zb migrate supabase --supabase-url https://<ref>.supabase.co --report supabase-import.json- Users keep their ids, emails, phones, confirmations, metadata and identities (Google, GitHub, …), and sign in with their old passwords: bcrypt and GoTrue’s argon2 hashes are verified, then re-hashed at the first sign-in. SAML SSO users are not imported (configure the identity provider; they are created at their next sign-in).
- Storage: every bucket with its settings and every object, through the Storage API with the service-role key
(or Supabase’s S3 endpoint with
--s3-endpointandSUPABASE_S3_ACCESS_KEY_ID/SUPABASE_S3_SECRET_ACCESS_KEY). Run the generatedsupabase-storage-owners.sqlin the SQL editor to restore file owners. - Functions in
supabase/functionsare bundled and deployed. Functions deployed on Supabase without source here are listed; fetch them withsupabase functions download <name>. Secret names are listed so you can set them again withzb secrets set NAME=value; values never leave Supabase. - The command uses a secret key of this project created for the run (it expires in two hours and is revoked at
the end), or
ZB_PROJECT_SECRET_KEYwhen set.
-
Grant auth hooks to
supabase_auth_hook: on Databasezy, Postgres auth hooks run as this role (it cannot touch theauthschema). The report lists functions granted tosupabase_auth_admin; grant them again:SQL editor grant execute on function public.custom_access_token_hook to supabase_auth_hook;grant select on public.user_roles to supabase_auth_hook;
The portal walks through the same steps under Platform → Auth → Import from Supabase.