PGX. Postgres for agents. π
One runtime. Thousands of Postgres databases.
# Claude Code: install inside a session (native plugin, lightweight repository)
/plugin marketplace add pgrundev/pgx-plugin
/plugin install pgx@pgx-plugin
# Any agent host (Claude Code, Cursor, Codex): the Agent Skills CLI
npx skills add pgrundev/pgx --skill pgx
# Manual PGX installation
npx pgxdb@latest
Every AI agent gets its own Postgres.
Install one Skill. Your coding agent handles the database.
No Docker required Β· Native Mac & Linux Β· Automatic cleanup
Open source Β· Free Β· v0.1.2 Β· For disposable databases, not production data
PGX is a database runtime for short-lived Postgres databases: agent sandboxes, CI and per-PR databases, test and preview environments. One PGX process holds many isolated databases. Each costs tens of kilobytes of memory while idle, is created in milliseconds from a sealed template, and is backed, on a copy-on-write storage backend such as ZFS, by a clone that starts at a few megabytes whatever the template's size (on a filesystem without block cloning a branch is a full copy).
PGX speaks the Postgres wire protocol and SQL dialect. Rails, Django, Prisma, SQLAlchemy and node-postgres run their full migrate β test β schema-change lifecycles on it unchanged.
PGX is the database engine behind PGRun branches.
A database should be cheap enough to allocate like an object, not provision like a server.
Agent workflows, test suites, CI jobs and preview environments create databases constantly and throw them away minutes or hours later. Giving each one its own PostgreSQL server costs a process, tens of megabytes and a cold start every time. Traditional PostgreSQL is built to keep one database running for years; PGX is built to create one in milliseconds, use it briefly and delete it.
Instead of
1 database β 1 PostgreSQL server
PGX runs
1 PGX runtime β hundreds or thousands of isolated databases
Immutable state is shared. Each database pays mainly for what makes it different.
~6.2 ms warm database mint, p50
~70 KB memory per untouched idle database
~128 KB memory per idle database that has been queried
1,000 isolated databases on a 4 vCPU / 8 GB VM
128β155 MB total runtime memory with 1,000 untouched databases
~213 MB after all 1,000 have been used
10 GB template
~674 ms copy-on-write branch ready
~13.6 MB initial additional physical storage
219 / 231 PostgreSQL src/test/regress byte-exact against PG 18.6 expected output
0 result differences (the other 12 differ in EXPLAIN plan text only)
These describe measured configurations and workloads, not a claim that PGX is faster than PostgreSQL in general. The regression figure is PostgreSQL's own test suite on one platform under the regress profile (see Benchmarks), not a compatibility percentage for arbitrary SQL. The detail and the scripts are under Benchmarks.
PGX is built around a different database lifecycle:
sealed template
β
mint database milliseconds, copy-on-write
β
agent / CI / test minutes or hours
β
discard dropped a grace period after the last session
A database is created from a sealed template without copying it. On Linux, ZFS block cloning lets a branch share every unchanged block with its template, so a branch of a 10 GB database is ready in under a second and starts at about 14 MB of its own storage.
A coding agent's database workload looks like this, and many agents do it at once:
create database β run migrations β change schema β load fixtures
β run tests β inspect results β throw the database away
PGX optimizes for that lifecycle rather than for a long-running production primary. Every agent, every pull request and every test run can have its own real Postgres-compatible database.
Measured on a 4 vCPU / 8 GB / $8.50-a-month VPS with ZFS block cloning. The
load generator ran on the same CPUs, so these are architecture numbers,
not capacity limits. Every figure has a script and a committed raw result
(benchmarks/pgx/linux/README.md).
| PGX | |
|---|---|
| 1000 idle databases in one runtime | 128β155 MB in total |
| 1000 databases, each queried once | 213 MB |
| Per idle database | ~70 KB untouched, ~128 KB once queried |
| 1000 databases with 100 active | 283 MB |
| Idle CPU with 1000 databases | 0.33β0.43 % of one core |
For comparison, one idle PostgreSQL 18 server uses 16.6 MB.
| p50 | p99 | |
|---|---|---|
| Database from the warm pool | 6.2 ms | 9.4 ms |
| Cold database (copy-on-write clone of a 600-file template) | 74 ms | 102 ms |
| 500 simultaneous new-database requests | all served, 0 errors | drained in 12.6β13.8 s |
Times run from the client's connection to its first SELECT 1 answer,
on the same host.
| Template | Branch ready in | New physical storage |
|---|---|---|
| 1 GB | 135 ms | 2.3 MB |
| 2 GB | 198 ms | 3.7 MB |
| 5.2 GB | 417 ms | 7.0 MB |
| 10 GB | 674 ms | 13.6 MB |
A branch of a 10 GB database is ready in under a second and initially uses hundreds of times less physical storage than a full copy.
| Runtime memory | |
|---|---|
| After 10,000 create β use β drop lifecycles | 108 MB |
| After 30,000 lifecycles | 111 MB (~0.2 KB retained per lifecycle) |
| Result | |
|---|---|
PostgreSQL regression suite (src/test/regress, PG 18.6 expected output) |
219 / 231 byte-exact; 12 differ only in EXPLAIN plan text; 0 result differences. Measured under the PGX profile with parallel workers and synchronous_commit restored (configs/pgx-regress-overlay.conf), the two settings the suite asserts on; under the shipped ephemeral profile those two settings alone make select_parallel and stats differ. One suite, one platform: not a compatibility score. |
Real Rails 8.1 app (migrations, fixtures, model tests, schema change, db:schema:load) |
passes; within 0β5 % of PostgreSQL 18 per phase |
| Django 5.2, Prisma 6, SQLAlchemy 2, node-postgres, Rails-shaped SQL | pass |
| All framework runs | 36 / 36 |
Transactional behaviour follows PostgreSQL, including transactional DDL and temporary tables:
BEGIN;
CREATE TEMP TABLE pgx_test (id integer PRIMARY KEY, value text);
INSERT INTO pgx_test VALUES (1, 'PGX works');
SAVEPOINT before_update;
UPDATE pgx_test SET value = 'changed' WHERE id = 1;
ROLLBACK TO SAVEPOINT before_update;
SELECT value FROM pgx_test WHERE id = 1; -- 'PGX works'
ROLLBACK;
SELECT to_regclass('pg_temp.pgx_test'); -- NULL: the table was rolled back- Memory limits. Session, database and runtime limits refuse runaway work with SQLSTATE 53200. The session stays usable, other databases see 0 errors, and the runtime does not crash.
- Connection limits. A neighbour running a 50-connection migration mix pushes another database's p99 latency to 9.8 ms. Capped at 1 / 2 / 5 connections per database, it stays at 1.2 / 2.1 / 4.0 ms.
- cgroups. The kernel is the last line: under a cgroup memory limit, the worst case is that one runtime is killed. The host survives.
PGX is not trying to beat Postgres on single queries.
- Short statements: about 0.05β0.15 ms slower than PostgreSQL 18; a point SELECT takes 0.25 vs 0.13 ms.
- Large single-threaded scans: 1.5β2.2Γ slower.
- Equal: COPY and 4-client throughput.
Whole framework lifecycles take 0β15 % longer than on PostgreSQL 18.
PGX concentrates on database creation latency, density, memory efficiency, copy-on-write storage, high churn and isolation between tenants. For application lifecycle workloads it stays close enough to PostgreSQL while making large numbers of isolated databases dramatically cheaper to keep alive.
The full report:
docs/pgx/linux-results.md. The frozen v0.1
numbers: docs/pgx/v0.1-baseline.md.
The central idea: share everything immutable, pay for divergence. It applies to memory and to storage alike.
-
Shared runtime. Databases are isolated by name, roles and limits inside one process with a thread per connection. A database costs its catalog caches, not a server.
-
Shared catalog state. Databases cloned from the same template share identical relation-cache entries. Idle databases are skipped by autovacuum.
-
Mint-on-connect. Connecting to
<prefix><template>__<token>creates that database from a sealed template and connects you to it. A warm pool of pre-created spares serves bursts. A janitor drops databases a grace period after their last session. -
Copy-on-write storage.
file_copy_method = cloneturns database creation into ZFS block clones, so a branch shares every unchanged block with its template and its cost grows with divergence:template βββ branch A β only changed blocks βββ branch B β only changed blocks βββ branch C β only changed blocks -
Limits at every level. Session, database and runtime memory limits inside the engine, connection limits per database, and a Linux cgroup around the runtime. Memory-heavy work fails with a PostgreSQL-style error instead of taking the host down:
session β database / runtime β Linux cgroup -
Machine-readable status.
pgrust_runtime_status()returns JSON with databases, sessions, memory against limits, and warm-pool and mint counters.
PGX is at v0.1.2, an early release with known limitations listed in the release notes. It already powers PGX branches in PGRun. The current focus is PostgreSQL compatibility, Rails/application compatibility, concurrency semantics, lifecycle correctness, predictable resource containment and real agent workloads. Correctness and reproducibility come before benchmark marketing: if PGX behaves differently from PostgreSQL for a supported workload, we want a reproducible test case (see Contributing).
PGX v0.1 is for disposable databases: agent, CI, PR, test and preview databases built from sanitized copies of production. Use it where losing a branch is acceptable.
It is not a production primary, a system of record, or an HA or PITR database:
- Not crash-safe. The PGX profile runs with
fsync = off. - One process. A runtime is one crash domain.
- No C extensions. Existing PostgreSQL C extensions cannot load; only the built-in contrib modules exist, pgvector among them. PL/Python, PL/Perl and PL/Tcl are not available.
Keep Postgres as the source of truth for data you can't afford to lose.
If you need a long-lived production database, use PostgreSQL or a managed
PostgreSQL service. If you need hundreds or thousands of databases that
live for minutes or hours, PGX is the workload we are building for. The
full list is in docs/pgx/v0.1-readiness.md.
PGX is the database engine behind PGX branches in PGRun. PGRun adds the control plane around it:
production / source database
β
safe copy with masking
β
sealed template
β
PGX
β
one branch per agent, PR or CI job
β
automatic expiry
PGRun handles orchestration, routing, templates, lifecycle, access and branch management. PGX is the runtime. They are separate projects.
One command installs PGX, starts it, creates an app database and prints its
connection URL. No Docker, Rust or system PostgreSQL; the native distribution
is self-contained and relocatable.
npx pgxdb@latestPGX is ready.
postgres://postgres:β¦@127.0.0.1:5432/appPoint DATABASE_URL at that URL. npx pgxdb with no arguments is pgx run;
every other pgx command works the same way through npx:
npx pgxdb url app # its connection URL
npx pgxdb ls # databases, TTL, expiry, size
npx pgxdb branch app agent-001 # a disposable 2-hour branch for one agent
npx pgxdb clone app pr-123 --ttl 5m # a 5-minute copy-on-write clone
npx pgxdb --json ls # machine-readable output for agents
npx pgxdb stopThe binary is downloaded once (SHA-256 verified) into ~/.pgx/dist/<version>
and reused. npm install -g pgxdb puts pgx on your PATH for good. Supported:
Apple silicon Macs and Linux (arm64, x86-64). Intel Macs are not supported yet.
Without Node, the shell installer does the same install:
curl -fsSL https://pgx.dev/install | sh
pgx runAny PostgreSQL client or ORM works against pgx url <db>. The full CLI,
install options, integrations (Rails, Node, Python, Go, psql), storage model
and troubleshooting are under docs/pgx/local-dev/; the
test matrix and release decision are in
docs/pgx/local-dev/validation-report.md.
Clones are fast branches: copy-on-write on a backend with block cloning (such as ZFS), a full copy elsewhere. Any database can also carry a TTL so it cleans itself up β a branch per agent, pull request or CI job that you never have to remember to delete.
pgx branch myapp agent-1 # an agent's own database; expires in 2 hours
pgx branch myapp agent-2 --ttl 30m # same, with a shorter TTL
pgx clone myapp pr-123 --ttl 30m # a clone; permanent unless you give a TTL
pgx create scratch --ttl 2h # a throwaway database, gone in 2 hours
pgx ls # shows each database's TTL and expiry
pgx ttl pr-123 unlimited # changed your mind β keep it for goodpgx branch is pgx clone for agents: a 2-hour TTL by default and the
connection URL as the last line of output, so an agent can be handed
DATABASE_URL="$(pgx branch myapp agent-1 | tail -1)" or read --json.
Databases created without --ttl are permanent. A background supervisor
drops each temporary database shortly after its deadline, never touches
permanent or system databases, and resumes cleanup automatically after a
pgx stop / pgx start. Details and the identity-safety model are in
docs/pgx/local-dev/ttl.md.
Install the Skill, then ask your agent. In Claude Code, from inside a session:
/plugin marketplace add pgrundev/pgx-plugin
/plugin install pgx@pgx-plugin
/pgx:pgx Set up isolated Postgres databases for this project.
pgrundev/pgx-plugin holds only
the plugin (about 110 KB, generated from this repository on every change to
main), so the install takes seconds. Adding this repository itself as the
marketplace still works (/plugin marketplace add pgrundev/pgx, then
/plugin install pgx@pgrundev) but clones about 2 GB. Install from one of
the two, not both.
For Cursor, Codex or several hosts at once, the Agent Skills CLI installs the same Skill into the project:
npx skills add pgrundev/pgx --skill pgxUse PGX to give this project isolated databases for coding agents. Set everything up.
The plugin is the Skill packaged for Claude Code's plugin manager (/plugin
lists, updates and removes it; the skill is /pgx:pgx and Claude also picks
it up on its own when a task touches the database). The Skills CLI copies
the Skill into .claude/skills/pgx and .agents/skills/pgx for every host.
Both contain no hooks and install no database engine: the pgx binary is
installed when the Skill first needs it, with your approval. Details and the
differences: docs/pgx/local-dev/claude-plugin.md.
The agent detects your database setup, installs PGX if needed, imports your
development database schema-only into a template (pgx copy), writes a
committed .pgx.toml, installs lifecycle hooks (pgx agent setup) and
proves a branch. From then on each Claude Code session gets its own branch
with DATABASE_URL already set, extended while the agent works and removed
after it stops, and every subagent it launches gets a branch of its own,
with its shell commands routed there; two agents never see each other's
changes and your PostgreSQL is never written. Installing the Skill executes
nothing by itself: the agent does the setup when the Skill is activated and
your permissions allow it.
| Host | Automatic isolation | Status |
|---|---|---|
| Claude Code | yes, through hooks: one branch per session and one per subagent | verified live |
| Cursor | hooks installed; pgx agent exec -- when PGX_ISOLATION is unset |
env propagation pending live verification |
| Codex | pgx agent exec -- codex (Codex hooks cannot set environment) |
wrapper verified |
Requires PGX 0.1.2 or newer (release v0.1.2; 0.1.1 isolates sessions but lets subagents share the parent's branch); the Skill checks the installed version before it does anything.
Details, the lifecycle, security rules and the validation notes:
docs/pgx/local-dev/agents.md. The Skill
itself: skills/pgx/SKILL.md.
The same thing without an agent. Copy PostgreSQL into PGX once, then give every agent its own branch. The source is only read; PostgreSQL stays your source of truth.
PGX_PORT=5433 npx pgxdb@latest # PGX beside PostgreSQL on 5432
# Import PostgreSQL once (pg_dump β pg_restore, read-only on the source)
pgx copy --from "$DATABASE_URL" --to myapp --schema-only
# Give each AI agent its own database (isolated, self-expiring after 2h)
pgx branch myapp agent-1
pgx branch myapp agent-2
pgx agent exec -- bin/rails test # or: any command in a branch of its own$ pgx branch myapp agent-1
β Branch created: agent-1
β Source: myapp
β Expires in: 2 hours
DATABASE_URL:
postgres://postgres:β¦@127.0.0.1:5433/agent-1--schema-only copies the structure without the rows (migration bookkeeping
such as schema_migrations is kept, so frameworks see their migrations as
applied). The copy needs pg_dump/pg_restore on the machine (a PostgreSQL
install has them); it refuses sources that use extensions PGX does not ship
(PostGIS, TimescaleDB, β¦) and never overwrites an existing database. Details,
limitations and measurements: docs/pgx/local-dev/copy.md.
You need the Rust toolchain (rust-toolchain.toml pins Rust 1.96.0;
rustup fetches it), the RE2 regex library, and the PostgreSQL 18 client
tools for initdb and psql:
- Debian/Ubuntu:
sudo apt-get install -y build-essential pkg-config libre2-dev, pluspostgresql-18andpostgresql-client-18from the PGDG apt repo (thenexport PATH="/usr/lib/postgresql/18/bin:$PATH"). - macOS:
brew install re2 pkg-config postgresql@18, thenexport PATH="$(brew --prefix postgresql@18)/bin:$PATH".
cargo build --release --locked --bin postgresThe server binary is target/release/postgres. Release builds refuse to
compile without RE2 on purpose: the fallback regex engine is much slower.
At run time the server loads two libraries with dlopen rather than
linking them: libxml2 for the xml type and SQL/XML functions, and ICU for
ICU collations. The PostgreSQL 18 packages above install both. A host
without libxml2 still starts, but XML functions raise unsupported XML feature (SQLSTATE 0A000) with a hint naming the library, and
pgrust_runtime_status() reports libraries.libxml2.available = false.
Debian/Ubuntu package: libxml2 (libxml2-16 from Ubuntu 25.10); macOS
ships libxml2 with the system.
# Create a cluster and apply the PGX profile.
initdb -D /tmp/pgx-data --no-locale --encoding UTF8 -U postgres
cat configs/pgx-ephemeral.conf >> /tmp/pgx-data/postgresql.conf
# The server reads timezone and other data files from a Postgres share dir.
export PGRUST_PGSHAREDIR=/usr/share/postgresql/18 # macOS: "$(brew --prefix postgresql@18)/share/postgresql"
export PGRUST_TZDIR=/usr/share/zoneinfo # macOS: "$PGRUST_PGSHAREDIR/timezone"
ulimit -s 65520
RUST_MIN_STACK=33554432 target/release/postgres \
-D /tmp/pgx-data -k /tmp -p 5432 \
-c listen_addresses= -c io_method=sync -c max_stack_depth=60000 \
-c pgrust.ephemeral_db_prefix=tdb_ \
-c pgrust.ephemeral_db_mint_roles=postgresPut the data directory on a ZFS dataset with block cloning enabled to get
copy-on-write branches (the profile sets file_copy_method = clone). On a
filesystem without block cloning, remove that line from the profile and
databases are created by copying files.
# Build a template once, then seal it.
psql -h /tmp -U postgres -c "CREATE DATABASE tpl_app"
psql -h /tmp -U postgres -d tpl_app -c "CREATE TABLE users (id bigserial PRIMARY KEY, email text UNIQUE)"
psql -h /tmp -U postgres -c "SELECT pgrust_seal_template('tpl_app')"
# Connecting to <prefix><template>__<token> creates that database.
psql -h /tmp -U postgres -d tdb_tpl_app__agent42 -c "INSERT INTO users (email) VALUES ('a@example.com')"
psql -h /tmp -U postgres -d tdb_tpl_app__agent43 -c "SELECT count(*) FROM users" # 0: isolated
# Runtime status as JSON.
psql -h /tmp -U postgres -Atc "SELECT pgrust_runtime_status()"Minted databases are dropped pgrust.ephemeral_db_grace seconds after
their last session ends, unless they are pinned with
pgrust_pin_database(). All control functions are listed in
docs/pgx/pgrun-interface.md.
The harness is Python 3 standard library only and talks the wire protocol directly. On a Linux host with a ZFS pool:
PGXBENCH_WORKDIR=/tank/pgx-bench benchmarks/pgx/linux/run-suite.sh scale churn pool cow regressResults land in benchmarks/pgx/results/<host>-<commit>/.
benchmarks/pgx/linux/README.md maps
every headline number above to its step, script and raw result.
The regression suite is PostgreSQL's own src/test/regress, vendored
unmodified at crates/postgres-18.6-reference/src/test/regress. It runs
with stock pg_regress (benchmarks/pgx/regress-baseline.py) under the
PGX profile plus configs/pgx-regress-overlay.conf (parallel workers and
synchronous_commit back on, because the suite asserts on them), and each
difference is classified as plan-only or semantic. The nightly run fails on
any semantic difference (classify-regress-diffs.py --fail-on-semantic).
| Document | What's in it |
|---|---|
v0.1-baseline.md |
the frozen v0.1 numbers and the build each came from |
v0.1-readiness.md |
what is ready, known limitations, approved use |
linux-results.md |
the full benchmark report |
compatibility-gate.md |
how to decide whether a database can run on PGX |
local-dev/claude-plugin.md |
the PGX plugin for Claude Code: /plugin marketplace add pgrundev/pgx-plugin, /plugin install pgx@pgx-plugin, the lightweight pgrundev/pgx-plugin repository and how it is generated, how it relates to the Skills CLI install, updates, removal, the Anthropic directory submission |
local-dev/agents.md |
the PGX Skill and pgx agent: automatic per-session isolation for Claude Code, Cursor and Codex, with the compatibility matrix |
local-dev/copy.md |
pgx copy: import an existing PostgreSQL database and branch it per agent |
pgrun-integration.md |
the host-agent API PGRun builds against |
lifecycle-memory.md, linux-churn.md |
memory under long churn, and what was fixed |
zfs-cow.md |
copy-on-write branch cost and the recordsize trade-off |
linux-limits.md |
memory limits and the cgroup backstop |
PGX is early, and the most useful contribution is a concrete compatibility case. If something works on PostgreSQL 18 and behaves differently on PGX, open an issue with:
PostgreSQL version
PGX version
minimal SQL reproduction
expected result
actual result
The areas where a case helps most: MVCC, locking, deadlocks, isolation levels, transactional DDL, ORMs (Rails, Django, Prisma, SQLAlchemy), schema migrations and client compatibility. Every real compatibility bug becomes a permanent regression test.
PGX is built on pgrust, a PostgreSQL-compatible database written in Rust. We are grateful to the pgrust project and its contributors for the foundation PGX builds on. PGX narrows that foundation to one problem, high-density disposable Postgres-compatible databases for agents and tests, and its own changes concentrate on shared database state, lifecycle and churn, density, memory containment, copy-on-write branching, fast minting and agent/test compatibility.
PGX is licensed under AGPL-3.0. Portions derived from PostgreSQL
remain under the PostgreSQL License; see NOTICE.
Production PostgreSQL is optimized to live for years. PGX is optimized to live for minutes.
One runtime. Thousands of isolated databases. Postgres for agents.