Migrate from Neon

Move a Neon Postgres database to SupaHost: dump it over Neon's direct (non-pooled) endpoint, load it into a project, replace the Neon serverless driver and verify.

Neon is standard Postgres, so a Neon database moves to SupaHost as a pg_dump file. The one change people miss is in the app: Neon's serverless driver talks to Neon's own proxy, while a SupaHost project is reached through the Data API (supabase-js or HTTP). Plan for that code change as well as the data copy.

Plan

  • Size. Check the database size in the Neon console. It must fit your plan's shared database quota (8 GB Builder, 25 GB Pro, 50 GB Business).
  • Branch. Dump from the Neon branch that holds production data, usually main.
  • Load path. On Business and Enterprise you restore over the SSH tunnel. On Builder and Pro you load through Studio, MCP or a script, or stage the dump in a Supabase Cloud project and use the one-click import.
  • Code. List every place that uses @neondatabase/serverless, pg or an ORM against DATABASE_URL.

Export from Neon

In the Neon console's Connect dialog, turn connection pooling off. The direct host has no -pooler in its name, which is what pg_dump needs.

pg_dump -Fc --no-owner --no-privileges -n public \
  "postgresql://bookings_owner:<password>@ep-quiet-dawn-123456.eu-central-1.aws.neon.tech/bookings?sslmode=require" \
  -f bookings.dump

Use version 17 client tools. A compute that has scaled to zero wakes up for the dump. -n public leaves out Neon's own schemas (such as neon_auth if you used Neon Auth); users move to Supabase Auth instead (see Moving Auth users between projects for the admin API approach).

Prepare the target

Create the extensions the dump relies on, in Studio's SQL editor:

create extension if not exists pg_trgm with schema extensions;
create extension if not exists vector with schema extensions;

Load

Business and Enterprise:

ssh -N -L 6543:127.0.0.1:5432 [email protected] &
pg_restore --no-owner --no-privileges -d "postgresql://postgres:<password>@127.0.0.1:6543/p_app_one" bookings.dump

Get the password from Dashboard → your project → Connect → Database → Show connection string. Read through any errors pg_restore prints; most come from objects that already exist.

Builder and Pro: restore the dump into a temporary Supabase Cloud project and run the one-click import, or apply the schema in Studio's SQL editor (pg_restore --schema-only -f schema.sql bookings.dump gives you the SQL) and load tables from CSV or with a supabase-js script using the service_role key.

Secure and tune

Enable RLS on every table in public, because the Data API exposes that schema. The policy below assumes guest_id holds Supabase Auth user IDs once your users have moved:

alter table public.bookings enable row level security;
create policy "guests see their bookings" on public.bookings
  for select to authenticated using ((select auth.uid()) = guest_id);
analyze;

Switch the app

Replace the Neon driver with supabase-js:

// before
const rows = await sql`select * from bookings where guest_id = ${userId}`

// after
const { data: rows, error } = await supabase
  .from('bookings')
  .select('*')
  .eq('guest_id', userId)

Queries that don't map cleanly to the query builder can become database functions called with supabase.rpc().

Verify

Run select count(*) on your main tables in both databases, exercise the key screens, and keep the Neon project until the new setup has run for a few days.

Updated 11 November 2026 · View as Markdown · Something wrong or missing? Tell us