A real PostgreSQL.
One pip install.
For your Python app, notebook, tests or AI agent. No server to run, no Docker. Agents get a read-only connection that PostgreSQL itself enforces, and your data can keep a copy in a bucket you own.
import dbcenter # starts PostgreSQL 18; the data stays in ./data db = dbcenter.DBCenter(path="./data") db.query("CREATE TABLE notes (body text)") db.query("INSERT INTO notes VALUES ($1)", ["milk"]) print(db.query("SELECT * FROM notes")) print(db.url) # for psql, psycopg, SQLAlchemy db.shutdown() # stops with your program
db.query("SELECT sum(total) FROM orders", read_only=True) # fine db.query("WITH x AS (DELETE FROM orders " "RETURNING *) SELECT 1", read_only=True) # refused by PostgreSQL itself: # cannot execute SELECT in a read-only transaction
# pip install dbcenter - and pytest has it def test_orders(dbcenter_db): dbcenter_db.query("CREATE TABLE orders (total int)") dbcenter_db.query("INSERT INTO orders VALUES (10)") n = dbcenter_db.query("SELECT count(*) AS n FROM orders") assert n[0]["n"] == 1 def test_sqlalchemy(postgres_url): engine = create_engine(postgres_url)
bucket = dbcenter.S3StorageAdapter("my-bucket", ...) laptop = dbcenter.DBCenter(path="./laptop", multi_user_sync=True) laptop.collection("notes").insert_one({"body": "hi"}) dbcenter.SyncEngine(laptop, bucket).sync_table("notes", 10_000) # any other machine rebuilds from the bucket dbcenter.SyncEngine(server, bucket).pull_and_apply("notes")
What you get
The actual postgres, started by your program.
Not an emulation and not a fork. DBCenter downloads PostgreSQL once, runs it as a child process against a data directory, and stops it with your program. Every SQL feature works.
Read-only for AI agents
read_only=True runs SQL in a read-only transaction that is rolled back. PostgreSQL refuses the write, however it is phrased. Nothing parses the SQL, so nothing can slip past.
An MCP server
dbcenter-mcp gives Claude, Cursor or any MCP client its own PostgreSQL. Read-only unless you pass --allow-writes.
A pytest fixture
Installing the package gives pytest dbcenter_db: one real server per session and an empty schema for every test. No Docker, no service container.
A copy in your own bucket
Each table ships what changed to S3, Cloudflare R2, Tigris or MinIO as Parquet, and another machine rebuilds from it. Lose the machine, keep the data.
Fast analytics from the same files
With columnar=True, aggregates read that Parquet through DuckDB: 4.5x to 8.7x faster than PostgreSQL on one core. Lookups stay on PostgreSQL.
Extensions in one call
install_extension("pgvector"), PostGIS, pg_trgm or pg_duckdb. Every download is checked against a pinned SHA-256 before it is loaded.
See it in action
Three things it is for.
Click each example to expand it. Every one runs as-is after pip install dbcenter.
SQL for an agent, without handing it your dataread_only=True, enforced by PostgreSQL
import dbcenter db = dbcenter.DBCenter(path="./data") db.query("CREATE TABLE IF NOT EXISTS orders (id serial, total numeric)") db.query("INSERT INTO orders (total) VALUES (10.5), (99)") print(db.query("SELECT sum(total) AS revenue FROM orders", read_only=True)) for sql in ["DELETE FROM orders", "WITH gone AS (DELETE FROM orders RETURNING *) SELECT count(*) FROM gone", "DROP TABLE orders"]: try: db.query(sql, read_only=True) except Exception: print("refused:", sql) db.shutdown()
pip install "dbcenter[mcp]", then claude mcp add dbcenter -- dbcenter-mcp --path /abs/path/to/data.Test against PostgreSQL, not something like itpytest fixtures, no Docker
import psycopg def test_orders(dbcenter_db): dbcenter_db.query("CREATE TABLE orders (id serial, total numeric)") dbcenter_db.query("INSERT INTO orders (total) VALUES (10)") assert dbcenter_db.query("SELECT count(*) AS n FROM orders")[0]["n"] == 1 def test_tables_do_not_leak(dbcenter_db): # every test starts with an empty public schema assert dbcenter_db.query("SELECT count(*) AS n FROM pg_tables WHERE tablename = 'orders'")[0]["n"] == 0 def test_your_own_driver(postgres_dsn): with psycopg.connect(postgres_dsn) as conn: assert conn.execute("SELECT 1").fetchone() == (1,)
postgres_url gives SQLAlchemy the same database.Your data in a bucket you ownS3, R2, Tigris or MinIO, as Parquet
import dbcenter bucket = dbcenter.S3StorageAdapter( "my-bucket", endpoint="https://<account>.r2.cloudflarestorage.com", access_key="...", secret_key="...", ) laptop = dbcenter.DBCenter(path="./laptop", multi_user_sync=True) laptop.collection("notes").insert_one({"body": "remember the milk"}) dbcenter.SyncEngine(laptop, bucket).sync_table("notes", 10_000) laptop.shutdown() server = dbcenter.DBCenter(path="./server", multi_user_sync=True) dbcenter.SyncEngine(server, bucket).pull_and_apply("notes") print(server.query("SELECT body FROM notes"))
Measured, not claimed
Performance that matters.
Against a plain PostgreSQL with the same limits: 1 CPU and 1 GB, the cheapest tier anyone rents. Re-run on 17 October 2026.
columnar=True, reading the synced Parquet through DuckDB
List prices, the same one-core machine in both, so only storage differs
The newest 100 of 200,000 rows: the same speed as PostgreSQL, because it is PostgreSQL.
A node that rebuilds from the bucket takes longer to come up.
About three times plain PostgreSQL. The bucket is for durability and analytics, not for serving requests.
The method, the rig and every number →
Try it in a minute.
A Colab notebook runs SQL, agent safety, pgvector memory and a bucket sync.
Architecture
Small on purpose.
Storage, planning and transactions are PostgreSQL's. DBCenter adds the lifecycle and the copy in your bucket.
- A real postgres binaryDownloaded once to
~/.dbcenter, then started against your data directory. - Stopped with your programA small watcher process shuts PostgreSQL down cleanly, even if your program crashes.
- An ordinary connection
db.query()from Python, ordb.urlfor psql, psycopg, SQLAlchemy and any driver in any language. - Parquet in your bucketOne column per field, so DuckDB can read it directly for analytics.
Alternatives
How it compares.
SQLite (+ Litestream)
Lighter and simpler. But it is not PostgreSQL: no pgvector, no PostGIS, and different SQL from the database you deploy.
testcontainers / Docker
Real PostgreSQL too, but it needs Docker on every laptop and CI runner, and it does not run in Colab.
pgserver, postgresql-wheel
The same idea in Python. Neither has been updated since 2024 and 2021, and they stop at PostgreSQL 16 and 14.
PGlite
PostgreSQL compiled to WASM, for JavaScript. A single connection, in the browser rather than in Python.