Migrating from Neon to Layerbase
Neon is plain Postgres, which makes the database half of this migration boring in the best way: a dump and a restore, and your tables, indexes, and rows land on the other side unchanged. The parts worth writing about are the two places people trip. The first is the connection string, because Neon hands most people a pooled endpoint that a straight pg_dump should not run through. The second is Neon Auth, which does not live in your database at all, so it does not travel the way you might assume. This post is the honest version of both.
If you are still deciding whether to leave, that is a different question and I wrote it up separately in Neon is now a Databricks product. Should you still use it?. This post assumes you have decided, and covers the move.
Just want it done? Start at layerbase.com/migrate/neon. You sign in, the wizard opens with Neon already selected, and it copies your schema and data in one pass: read-once, nothing written back to Neon. If you only have a pooled connection string, paste it anyway and it switches to the direct endpoint for you. The rest of this post is that same migration explained step by step, plus the manual path.
Contents
- What moves and what does not
- The migration, step by step
- The connection string gotcha
- Neon Auth: the honest part
- You keep your branching workflow
- Pointing your app at the new database
- The manual path
What moves and what does not
Be clear about scope before you start.
Moves automatically:
- Your
publicschema and any other application schemas: every table, index, constraint, and row. - Sequences, views, and functions that live in your own schemas.
- The
neon_auth.users_synctable, if you use Neon Auth. It is a normal table in your database, so it copies like any other. Read the Neon Auth section before you rely on that, though, because the passwords are not in it.
Does not move:
- Neon Auth credentials and sessions. Passwords and active sessions live in Stack Auth, the hosted service behind Neon Auth, not in your Postgres. There is no table to copy them from, so they cannot come across. Your users keep their profiles and set a new password on first sign-in.
- Neon-specific control-plane features: your branch topology, compute autoscaling settings, and the point-in-time restore history are Neon platform state, not database rows. The data on your primary branch moves; the branching structure is something you recreate (which is cheap, see below).
If your app is a Postgres database with tables and data, the whole thing moves. Auth is the part that needs a plan, and it needs one whether you migrate or not, because Neon Auth was always a separate hosted service bolted onto the database.
The migration, step by step
The fastest path is the built-in migration flow. It runs the dump and restore for you and handles the pooled-endpoint problem automatically.
Step 1: Start the migration
In the Layerbase dashboard, click New database and choose Migrating from another platform, then pick Neon. (You can also open the Migrate tab on an existing blank database.)
Paste a Neon API key (starts with napi_, from the Neon console under Account Settings, then API Keys). Layerbase uses it to list your projects so you can pick the one to move. The key is used for the copy and is not stored afterward.
Step 2: Let it copy
Layerbase provisions a Postgres instance and runs the dump and restore for your schemas. For a database under 1GB this is a couple of minutes. You end up with a running Layerbase Postgres holding your tables and data, on a plan whose price does not move with compute-hours.
Step 3: Grab the connection string
Open the new database in the dashboard and copy its connection string. It looks like:
postgresql://layerbase:<password>@your-host.cloud.layerbase.dev:5432/app?sslmode=requireThat is a standard, direct Postgres URL with TLS. Your existing drivers and ORMs take it as-is.
The connection string gotcha
This is the one that costs people an hour, so it is worth calling out on its own.
Neon gives you two connection strings, and by default the console shows the pooled one. You can tell them apart by the host: the pooled endpoint has -pooler in it (ep-cool-name-123456-pooler.region.aws.neon.tech), the direct one does not. The pooled endpoint runs through PgBouncer in transaction mode, which is great for serverless apps and wrong for a migration: pg_dump opens session-level constructs that transaction pooling does not support, and the dump either errors or comes out incomplete.
The built-in flow handles this for you. If you paste a pooled string, Layerbase rewrites it to the direct endpoint before copying, so you do not have to think about it. If you drive the migration by hand (see The manual path), strip the -pooler from the host yourself and dump against the direct endpoint.
Neon Auth: the honest part
If you never turned on Neon Auth, skip this section: you have a plain Postgres database and nothing here applies.
If you did use it, here is the part people get wrong. Neon Auth is Neon's integration of Stack Auth, a hosted authentication service. It syncs a read-only mirror of your user profiles into a neon_auth.users_sync table so you can join against it in SQL, but the actual credentials, the password hashes and the session state, live inside Stack Auth's service, not in your database. Your database only ever held a copy of the profile fields.
So when you migrate the database, you get the neon_auth.users_sync table like any other table: emails, names, IDs, timestamps. What you do not get is anything you can log a user in with, because that never lived in Postgres to begin with. There is no password hash to copy.
The practical consequence: your users keep their identity (the profile row is right there), but they set a new password the first time they sign in on the new side. That is a re-onboarding, not a data loss, and it is the same thing that would happen if you moved off Neon Auth while staying on Neon. If a no-reset auth migration is what you need, it exists for providers that keep credentials in the database (for example, moving from Supabase carries the bcrypt hashes across because Supabase stores them in auth.users). Neon Auth is architecturally the other kind, and no tool can copy a secret out of a service that does not expose it.
You keep your branching workflow
Branching is the Neon feature people worry about giving up, so to be clear: Layerbase Postgres branches too. If you use Neon branches for preview deploys or to test a migration against production-shaped data, that workflow survives. You migrate your primary branch, then branch it on Layerbase the same way, from the dashboard or the CLI. The branch topology itself does not carry over in the copy (it is platform state, not data), so you recreate the branches you still want, which is a few clicks per branch and usually a shorter list than what accumulated on Neon.
Pointing your app at the new database
The database swap is one environment variable. Change DATABASE_URL to the Layerbase connection string and deploy. If your code used Neon's serverless driver (@neondatabase/serverless) over HTTP, you can switch to a standard pg or postgres client against the new TLS URL, or keep a driver that speaks plain Postgres. There is no proprietary client to keep.
Verify before you cut over. Run the app against the new database in staging and spot-check row counts against Neon:
select schemaname, relname, n_live_tup
from pg_stat_user_tables
order by n_live_tup desc;The counts should match. If they do, flip production by shipping the env var change. If you used Neon Auth, that is also when you wire the new sign-in path, because your app can no longer call Stack Auth's hosted endpoints.
The manual path
If you would rather drive the dump yourself, the standard Postgres move works, with the one caveat from above: dump against the direct endpoint, not the pooler.
pg_dump \
--no-owner --no-acl \
"postgresql://<user>:<password>@ep-cool-name-123456.region.aws.neon.tech/dbname?sslmode=require" \
> neon-dump.sql
psql \
"postgresql://layerbase:<password>@your-host.cloud.layerbase.dev:5432/app?sslmode=require" \
< neon-dump.sqlNote the host in the source string has no -pooler in it. If yours does, remove it. --no-owner --no-acl strips ownership and grants that would otherwise break the restore on a database with different roles.
Develop against a local copy
SpinDB runs Postgres locally with no Docker and accepts the same dump file, so you can rehearse the whole migration on your machine before you touch production.
npm i -g spindb
spindb create neon-test --start --connect
psql "$(spindb url neon-test)" < neon-dump.sqlRestore the dump locally, point your app at spindb url neon-test, and confirm everything works. When you are ready, create the cloud database and run the real migration.
The move off Neon is a dump, a restore, and an env var, on flat pricing that does not meter your compute. The database is the easy part and it moves cleanly. The two things to get right are dumping against the direct endpoint, not the pooler, and being honest with yourself about Neon Auth: the profiles come with you, the passwords were never yours to copy, and your users set a new one once. If you are still weighing the decision itself, the case for leaving Neon is the companion to this how-to.