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
sqltagged-template call from@vercel/postgresand every ORM usingPOSTGRES_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.