Skip to content

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.

Platform capabilities by service, with their status
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.

  1. 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 shop
    zb instances create --engine postgres --engine-version 17 --size s1 --region us-east --name shop-db --wait

    Note the instance id it prints (inst_...), then choose shop-db as the primary database under Project settings.

  2. 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.

  3. 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 nothing
    zb migrate --from "postgresql://postgres.<ref>:<password>@aws-0-us-east-1.pooler.supabase.com:5432/postgres" \
    --to inst_01jb... --mode copy_and_sync

    Details, troubleshooting and rollback: Migrate from Supabase.

  4. Recreate scheduled jobs. pg_cron jobs live in the skipped cron schema. Add them again in Cron or with select cron.schedule(...).

  1. Turn on the Data API with the schemas your app uses (usually public) under Platform → Data API.

  2. Create keys and keep the secret key on servers only:

    shell
    zb projects keys create --name web --kind publishable
    zb projects keys create --name server --kind secret
  3. Regenerate types from the new project:

    shell
    zb gen types --output src/lib/database.types.ts
  4. 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.

  5. Cut over when zb migrate status reports ready_for_cutover: stop writes on Supabase, run zb migrate cutover <mig_id>, deploy the new configuration.

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.

  1. Turn on Auth for the project (Platform → Auth) and, for files, Storage.

  2. 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 connection
    export SUPABASE_SERVICE_ROLE_KEY='<service_role key>' # Project settings → API
    export SUPABASE_ACCESS_TOKEN='sbp_...' # optional: lists deployed functions and secret names
  3. 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
  4. 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-endpoint and SUPABASE_S3_ACCESS_KEY_ID / SUPABASE_S3_SECRET_ACCESS_KEY). Run the generated supabase-storage-owners.sql in the SQL editor to restore file owners.
    • Functions in supabase/functions are bundled and deployed. Functions deployed on Supabase without source here are listed; fetch them with supabase functions download <name>. Secret names are listed so you can set them again with zb 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_KEY when set.
  5. Grant auth hooks to supabase_auth_hook: on Databasezy, Postgres auth hooks run as this role (it cannot touch the auth schema). The report lists functions granted to supabase_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.