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

Source: https://supahost.app/kb/migrate-from-neon · Updated 2026-11-11

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.

```bash
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](/kb/migrate-auth-users-between-projects) for the admin API approach).

## Prepare the target

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

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

## Load

**Business and Enterprise:**

```bash
ssh -N -L 6543:127.0.0.1:5432 supahost@acme.supahost.space &
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](/kb/migrate-from-supabase-cloud), 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:

```sql
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:

```ts
// 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.

## Related

- [Importing data (CSV, SQL dumps)](/docs/database/import-data)
- [Calling database functions (RPC)](/docs/api/rpc)
- [SSH access and direct Postgres](/docs/platform/ssh)
- [Can I connect from serverless and edge functions?](/kb/serverless-edge-connections)
