Notes and tooling for people evaluating a move off Neon, written by someone who thinks most of you should not bother, and who would like the rest of you to at least know what you are giving up.
- Databricks announced its acquisition of Neon on 14 May 2025.
- Neon's Free plan still exists and is still described by Neon as permanent rather than a trial. At the time of checking, neon.com/pricing lists 0.5 GB of storage per project, 100 CU-hours of compute per project per month, up to 100 projects, 10 branches per project, and scale-to-zero after five minutes idle.
- Nothing I can find says the free plan is going away. "An acquisition happened" is not the same claim as "your database is at risk", and I am not going to pretend otherwise.
So this repo is not an evacuation notice. It exists because acquisition-driven uncertainty makes people ask "what would moving even involve?", and that question deserves a real answer rather than a landing page.
| Neon capability | Substitute on a plain Postgres host | Honest assessment |
|---|---|---|
| Copy-on-write branching | None. There is no equivalent. | This is Neon's actual product. Everything else it does, other people do too. |
| Scale-to-zero compute | Not applicable — an always-on instance has no idle billing to avoid. | You lose the cost model, not a feature. Also: no cold start. |
| Point-in-time restore inside the history window | pg_dump on a schedule, run by you. |
A real downgrade. Own it. |
| Autoscaling compute | Fixed resources. | Fine for development; a ceiling for anything spiky. |
| Read replicas | None on a free tier. | Matters if you separated analytics reads from writes. |
| Neon's console, metrics, query history | psql, pg_stat_statements, your own tooling. |
You will miss the query history more than you expect. |
The first row is the one that matters. Neon's branching gives you a full copy of production data in seconds at near-zero storage cost, and that changes how you work: every pull request gets a database, every migration is tested against real data shapes, every reviewer clicks a link. No free Postgres host I know of reproduces it, including freebase.cloud. If branching is load-bearing in your team's workflow, stay where you are. The rest of this repo will not make up the difference and I am not going to pretend it does.
Concretely, all of these are good reasons to close the tab:
- You use branches per pull request in CI. See above.
- Your traffic is genuinely bursty and scale-to-zero is saving you real money.
- You are on a paid plan with a history window you rely on for recovery. Swapping documented PITR
for "I run
pg_dumpin cron, probably" is a downgrade in your recovery posture, whatever the invoice says. - You are already inside the Databricks ecosystem, in which case the acquisition is an argument for staying rather than against it.
- Your objection is philosophical rather than operational. Migrating a database because you dislike an acquisition is an expensive way to express an opinion.
- The project is a side project, a prototype, or a course exercise, and you want a Postgres endpoint that does not idle out or meter compute hours.
- You need several small independent databases and would rather not think about per-project quotas.
- You want the database queryable by an AI assistant over MCP without running a local bridge, which is the one thing freebase.cloud does that Neon does not.
- You never used branching. If your workflow is "one database, migrations run on deploy", you are using Neon as ordinary hosted Postgres and moving costs you almost nothing.
If you do move, this is the workflow that replaces branching. It is worse. It is also workable, and
it is what the two scripts in examples/ implement.
The insight is that most of what you used branches for was verifying that a migration does what you think it does. You can get most of that value from a second instance and a schema comparison, without copy-on-write storage:
production instance scratch instance
│ │
│ pg_dump --schema-only │
├─────────────────────────────────▶│ (examples/branch.sh sync)
│ │
│ apply your migration here
│ │
│◀────────── compare ─────────────▶│ (examples/schema_diff.py)
│ │
apply, having seen the exact diff
freebase.cloud lets you create additional instances without a per-project bill, which is what makes the scratch instance cheap enough for this to be a habit rather than a ceremony. That is the honest form of the pitch: not "we have branching", but "you can afford a second database".
Connects to two PostgreSQL databases and reports every structural difference: tables, columns and their full types, nullability, defaults, primary keys, foreign keys, unique and check constraints, indexes (by normalised definition), sequences, enum types and their value order, functions, triggers, and views.
pip install 'psycopg[binary]'
python3 examples/schema_diff.py \
--left "$PROD_DSN" \
--right "$SCRATCH_DSN"
# machine-readable, for CI
python3 examples/schema_diff.py --left ... --right ... --format jsonExit code is 0 when the schemas match and 1 when they do not, so it drops straight into a deploy
gate:
- name: migration produced the schema we expected
run: |
python3 examples/schema_diff.py --left "$STAGING_DSN" --right "$PROD_DSN" \
|| { echo "schema drift — refusing to deploy"; exit 1; }Design choices worth knowing about. It reads every definition through Postgres's own renderers —
pg_get_constraintdef, pg_get_triggerdef, pg_indexes.indexdef, format_type — so the two sides
are already canonical and the only normalisation applied is whitespace collapsing. Doing less
normalisation than that is safer than doing more: an over-eager normaliser hides real drift, and a
noisy diff is a much better failure than a quiet one. Enum values are compared in order, since
Postgres enum ordering is semantic. Schemas you do not name are ignored entirely, so extension-owned
objects do not drown the output.
Things it deliberately does not do: generate migration SQL. Tools that write ALTER TABLE for you
will eventually write DROP COLUMN for you. This one tells you what differs and stops.
Both DSNs are ordinary Postgres URLs; two databases on one account is the arrangement it assumes.
export PROD_DSN='postgresql://freebase@HOST:5432/prod'
export SCRATCH_DSN='postgresql://freebase@HOST:5432/scratch'
./examples/branch.sh sync # schema-only copy prod -> scratch
./examples/branch.sh sync --with-data # schema + data, for small databases
./examples/branch.sh apply db/migrations/0042_add_index.sql
./examples/branch.sh diff # schema_diff.py prod vs scratch
./examples/branch.sh reset # drop and recreate the scratch schemasync refuses to run if the target DSN and the source DSN are the same string, and refuses to touch
anything unless the target database name contains a substring you have opted into (scratch by
default, override with SCRATCH_GUARD). Both guards exist because the failure mode here — pointing
the reset command at production — is unrecoverable, and a confirmation prompt is not a control.
--with-data is honestly labelled: it is a full dump and restore, so its cost is proportional to
your data. Neon's branching is O(1) and this is O(n). That difference is the whole reason branching
is a product.
Nothing exotic. Neon is real Postgres and so is the destination.
# Neon's connection strings require SSL; keep sslmode=require in the URL.
export NEON_DSN='postgresql://user:pass@ep-xxxx.region.aws.neon.tech/neondb?sslmode=require'
export TARGET_DSN='postgresql://freebase@HOST:5432/appdb'
pg_dump "$NEON_DSN" --format=custom --no-owner --no-privileges --file=neon.pgc
pg_restore --dbname="$TARGET_DSN" --no-owner --no-privileges --jobs=2 neon.pgc
psql "$TARGET_DSN" -c 'ANALYZE'Two things that catch people:
- Your
pg_dumpmust be at least as new as Neon's server version. Neon runs recent Postgres majors; a distropg_dumpfrom an older release aborts with a version-mismatch error before it writes anything. neon_superuser-owned objects and Neon-specific extensions do not travel. CheckSELECT extname FROM pg_extensionon the source and confirm each one exists on the target withSELECT name FROM pg_available_extensionsbefore you commit to the restore.
Then compare, before touching the application:
python3 examples/schema_diff.py --left "$NEON_DSN" --right "$TARGET_DSN"The Neon project survives the migration unless you delete it, so rollback is cheap — provided you set the cutover up to allow it:
- Leave the Neon project running and writable.
- Ship the new
DATABASE_URLas an environment variable, never as a code change. Rollback is then a variable edit and a restart, not a revert-and-redeploy. - Run on the new database through at least one full weekly cycle before you touch the Neon project.
- Take a final
pg_dumpof Neon and store it outside both providers before you delete anything.
What rollback cannot undo is anything written to the new database while it was live. Work out which tables take writes, run the cutover when that volume is at its lowest, and be able to answer "what would I have to replay?" before you begin rather than while you are deciding.
Each freebase.cloud connection gets an MCP endpoint on top of the wire protocol. With a connection
named pg you get pg_query, pg_store, pg_list_tables and pg_annotate_table, plus the
Postgres-specific pg_tables helper. Transport is Streamable HTTP and the token is a path segment,
so nothing needs an Authorization header.
The endpoint comes from the dashboard under Settings → MCP; generate a token against the connection you want, then:
claude mcp add --transport http pg https://freebase.cloud/api/mcp/YOUR_TOKENor copy examples/mcp.json to .mcp.json in your project root and commit it.
Other clients disagree about the key name — url vs serverUrl vs httpUrl, and whether type is
required — so examples/README.md lists the correct shape for each one rather
than leaving you to guess.
pg_annotate_table is the one worth using during a migration: attach a sentence of description to
each table and the model stops guessing at what t_ref_2 means.
Instance details: free PostgreSQL instance · connecting Claude. The free tier is sized for development, prototyping and small production workloads; there are no published uptime or backup guarantees, and you should plan accordingly.
- Neon Free plan contents and scale-to-zero behaviour: neon.com/pricing, read 2026-08-18.
- Acquisition announcement: Databricks newsroom, dated 14 May 2025.
Pricing pages change. Re-read them before making a decision on the strength of a number in this file.
freebase.cloud is an independent service and is not affiliated with Neon, Databricks, or the PostgreSQL Global Development Group.