On Neon, you test a migration on a writable copy of production data first. If the change still breaks something after it ships, you roll the root branch back to any point in the history window. Branches are copy-on-write, so testing against a 100 GB database doesn't require 100 GB of new storage.
Test on a branch first
Create a branch from your production branch, run the migration there, and point a staging deployment at it. The branch has its own compute, and creating it adds no load to the parent, so production traffic isn't affected. The examples below use production, the default branch name for projects created in the Console; projects created with the CLI or API call it main.
neon branches create --name test/add-user-flags --parent production
# Run the migration against the new branch
psql "$(neon connection-string test/add-user-flags)" -f migrations/2026-04-25-add-flags.sqlRestore if a deployed migration breaks production
Lakebase Postgres keeps a continuous change history as Postgres WAL. Instant restore returns a root branch to a timestamp before the bad change ran (child branches don't support instant restore; you reset them from their parent instead):
neon branches restore production ^self@2026-04-25T14:32:00Z \
--preserve-under-name production_old_pre_migrationThe history window is 6 hours on the Free plan, up to 7 days on the Launch plan, and up to 30 days on the Scale plan. Paid plans default to 1 day, so raise it on production projects.
A safer migration pattern
- Create a
test/branch fromproduction. Run the migration and check that your app works against it. - Apply the same migration to
productionin a controlled deploy. - If something downstream breaks within the history window, branch from
productionat the pre-migration timestamp to inspect it, or restoreproductionin place.--preserve-under-namekeeps the pre-restore state as a backup branch.
Restore in place drops writes
Restoring production in place overwrites the branch's current state. Writes made after the target timestamp are removed from production and remain only in the backup branch. If you need them, copy them back from there, or branch from the timestamp instead of restoring in place.
Protected branches
On the Launch and Scale plans, mark production as a protected branch. A protected branch can't be deleted, reset, or archived, and branches created from it get new Postgres role passwords. On the Scale plan, you can also limit connections to protected branches with IP Allow. Launch supports up to 2 protected branches and Scale up to 5.
How other Postgres options compare
-
Amazon Aurora. Aurora cloning gives you a writable copy-on-write copy of production data to test against. Rollback after a bad change uses point-in-time restore, which creates a new cluster that your app then has to point at.
-
Amazon RDS for Postgres. There's no copy-on-write clone. Testing against real data usually means restoring a snapshot to a new instance, which materializes the full dataset. Rollback uses point-in-time restore within a retention period of up to 35 days, which also creates a new instance.
-
Supabase. Preview branches run your migrations in a separate environment. Branches from the GitHub integration start without production data; dashboard branches (public alpha) can include a copy of production data if the project has the PITR add-on. Rollback uses daily backups (Pro and above) or the paid PITR add-on, and the project is inaccessible while a restore runs.

Create a Neon branch, run your migration, throw the branch away.








