Migrate from Vercel Postgres

Move a Vercel Postgres (Neon-backed) database to SupaHost: dump it with the non-pooling URL, load it into a project and replace @vercel/postgres with supabase-js in your Vercel app.

Vercel Postgres databases run on Neon, and Vercel has moved existing stores over to Neon's integration. So the data side is a Neon-style pg_dump, and the app side is replacing @vercel/postgres with supabase-js. Your Next.js app can stay on Vercel; only the database moves.

Plan

  • Size. Check the database size in the Vercel or Neon dashboard. It must fit your SupaHost plan's shared database quota (8 GB Builder, 25 GB Pro, 50 GB Business).
  • Load path. Business and Enterprise restore directly over the SSH tunnel. On Builder and Pro, stage the dump in a Supabase Cloud project and use the one-click import, or load smaller databases through Studio.
  • Code. Find every sql tagged-template call from @vercel/postgres and every ORM using POSTGRES_URL. Vercel Functions can't open a Postgres connection to a SupaHost project, so these become supabase-js calls.

Export

Pull the env vars locally and dump with the non-pooled URL:

vercel env pull .env.migration
source .env.migration
pg_dump -Fc --no-owner --no-privileges -n public "$POSTGRES_URL_NON_POOLING" -f shop.dump

Use pg_dump 17. The pooled POSTGRES_URL goes through a transaction pooler and isn't suitable for dumps.

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" shop.dump

Copy the password from Dashboard → your project → Connect → Database → Show connection string.

Builder and Pro: restore shop.dump into a temporary Supabase Cloud project, then run the one-click import. For a few small tables, pg_restore --schema-only -f schema.sql shop.dump gives SQL to run in Studio's SQL editor, and the table editor can import each table from CSV.

Secure the tables

alter table public.orders enable row level security;
create policy "customers read own orders" on public.orders
  for select to authenticated using ((select auth.uid()) = customer_id);
analyze;

Switch the app

In Vercel → your project → Settings → Environment Variables, add:

NEXT_PUBLIC_SUPABASE_URL=https://app-one.acme.supahost.space
NEXT_PUBLIC_SUPABASE_ANON_KEY=eyJhbGciOi...
SUPABASE_SERVICE_ROLE_KEY=eyJhbGciOi...   # no NEXT_PUBLIC_ prefix: server only

Then replace queries:

// before (@vercel/postgres)
const { rows } = await sql`select id, total from orders where customer_id = ${userId}`

// after (supabase-js, in a Server Component or Route Handler)
const { data: rows, error } = await supabase
  .from('orders')
  .select('id, total')
  .eq('customer_id', userId)

Use @supabase/ssr's createServerClient on the server so RLS sees the signed-in user. If you add Supabase Auth, add https://*.vercel.app/** to the project's redirect URLs (Dashboard → your project → Auth tab) so preview deployments can sign in.

Verify

Compare select count(*) on each table, deploy a preview, and click through checkout or whatever your main flow is. Remove the Vercel Postgres store only after production has run on SupaHost for a while.

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