A Rust rewrite of Postgres.
New: Video of Michael's talk at PlanetScale on pgrust — and the slides.
How AI Changes the Economics of JIT Compilers
v0.4-beta is out: the release post and what's new.
pgrust is a re-implementation of Postgres meant to show what Postgres would look like if it was built in 2026. It is wire compatible and even SQL dialect compatible with Postgres.
Every line is Rust, written to match the behavior of the C implementation. Rust makes it easy to re-architect several core Postgres pieces. pgrust has:
- Gravity, a new vectorized push-based, JIT compiled query engine
- A thread based concurrency model
- A query scheduler designed to keep any individual query from taking down your database
- An automatic memory manager: one memory budget, sized from the machine, covers the buffer pool, the columnar caches and running queries, and gives pgrust control of what happens when you're running low on memory, greatly reducing the chance of your whole DB being taken down by the OS OOM killer
and many other really awesome pieces. The scheduler and the memory manager go after two of the four horsemen behind thousands of Postgres outages.
In your browser: https://pgrust.com, a full pgrust server compiled to WebAssembly, no install.
On your machine: download a release tarball from
https://pgrust.com/downloads/v0.4-beta/. Each one is self-contained: it
carries pgrust's own bin/postgres, bin/initdb and bin/psql plus the
share files they read, so you do not need a PostgreSQL install. The Linux
binaries need glibc 2.34 or newer (Debian 12, Ubuntu 22.04, RHEL/Rocky 9 and
later); the macOS binaries need macOS 11 or newer.
| Tarball | Runs on |
|---|---|
pgrust-0.4-beta-linux-x86_64.tar.gz |
any x86-64 CPU |
pgrust-0.4-beta-linux-aarch64.tar.gz |
any 64-bit Arm CPU |
pgrust-0.4-beta-linux-x86_64-znver3.tar.gz |
tuned for AMD Zen 3 and newer |
pgrust-0.4-beta-linux-aarch64-neoverse-v2.tar.gz |
tuned for Neoverse-V2 (AWS Graviton4, NVIDIA Grace) |
pgrust-0.4-beta-macos-arm64.tar.gz |
macOS on Apple Silicon |
pgrust-0.4-beta-macos-x86_64.tar.gz |
macOS on Intel |
pgrust-0.4-beta-macos-universal.tar.gz |
macOS, either (both architectures in one binary) |
The Linux builds are profile-guided optimized. The tuned builds use
instructions only those CPUs (and newer ones) have; when in doubt, take the
generic build. Every tarball has a .sha256 file next to it, and
MANIFEST.txt lists
them all.
# Pick your build: linux-x86_64, linux-aarch64, linux-x86_64-znver3 or
# linux-aarch64-neoverse-v2 (see the table above).
PLATFORM=linux-x86_64
# Download pgrust, verify the checksum and unpack it.
curl -LO "https://pgrust.com/downloads/v0.4-beta/pgrust-0.4-beta-$PLATFORM.tar.gz"
curl -LO "https://pgrust.com/downloads/v0.4-beta/pgrust-0.4-beta-$PLATFORM.tar.gz.sha256"
sha256sum -c "pgrust-0.4-beta-$PLATFORM.tar.gz.sha256"
tar xzf "pgrust-0.4-beta-$PLATFORM.tar.gz"
cd pgrust-0.4-beta-linux-*/
# Create a data directory with pgrust's initdb.
bin/initdb -D /tmp/pgrust-data --no-locale --encoding UTF8 -U postgres
# Start the server. It runs in the foreground and logs to this terminal.
ulimit -s 65520
RUST_MIN_STACK=33554432 bin/postgres \
-D /tmp/pgrust-data \
-k /tmp -p 5432 \
-c listen_addresses= \
-c io_method=sync \
-c max_stack_depth=60000Leave the server running and connect from a second terminal, in the same directory:
bin/psql -h /tmp -p 5432 -U postgres -c "select version()"You should see PostgreSQL 18.6 (pgrust 0.4-beta, build <commit>) on ...,
where <commit> is the short commit the binary was built from.
bin/postgres --version prints pgrust 0.4-beta (PostgreSQL 18.6 compatible).
The prebuilt image on Docker Hub,
malisper/pgrust, is a drop-in
replacement for the official postgres image: same env vars
(POSTGRES_PASSWORD, POSTGRES_USER, POSTGRES_DB, PGDATA, ...), same
/docker-entrypoint-initdb.d init scripts, same data volume at
/var/lib/postgresql/data. It carries the same postgres and initdb
binaries as the Linux release tarballs above, and initializes the cluster with
pgrust's own initdb; the only C PostgreSQL component in the image is psql
for the init scripts. malisper/pgrust:v0.4-beta is multi-arch (amd64 +
arm64); v0.4-beta-amd64 and v0.4-beta-arm64 pin one architecture.
docker run -d --name pgrust -e POSTGRES_PASSWORD=secret -p 5432:5432 malisper/pgrust:v0.4-betaThen connect, with your own psql or the one in the image:
psql -h localhost -p 5432 -U postgres
docker exec -it pgrust psql -U postgresmalisper/pgrust:latest still points at v0.3; use the v0.4-beta tag to get
this release. A data volume written by v0.3 holding columnar tables needs the
dump and restore described under
Stopping, restarting, cleaning up.
# Pick your build: macos-arm64 (Apple Silicon), macos-x86_64 (Intel) or
# macos-universal (both).
PLATFORM=macos-arm64
# Download pgrust, verify the checksum and unpack it. Download with curl: curl
# does not set the quarantine flag a browser download gets, so Gatekeeper stays
# out of the way (see the note below).
curl -LO "https://pgrust.com/downloads/v0.4-beta/pgrust-0.4-beta-$PLATFORM.tar.gz"
curl -LO "https://pgrust.com/downloads/v0.4-beta/pgrust-0.4-beta-$PLATFORM.tar.gz.sha256"
shasum -a 256 -c "pgrust-0.4-beta-$PLATFORM.tar.gz.sha256"
tar xzf "pgrust-0.4-beta-$PLATFORM.tar.gz"
cd "pgrust-0.4-beta-$PLATFORM"
# Create a data directory with pgrust's initdb.
bin/initdb -D /tmp/pgrust-data --no-locale --encoding UTF8 -U postgres
# Start the server. It runs in the foreground and logs to this terminal.
ulimit -s 65520
RUST_MIN_STACK=33554432 bin/postgres \
-D /tmp/pgrust-data \
-k /tmp -p 5432 \
-c listen_addresses= \
-c io_method=sync \
-c max_stack_depth=60000Leave the server running and connect from a second terminal, in the same directory:
bin/psql -h /tmp -p 5432 -U postgres -c "select version()"A note on Gatekeeper: the macOS binaries are signed ad hoc, not notarized
by Apple. Downloaded with curl as above, they are never quarantined and none
of this comes up. If you download with a browser instead, macOS refuses to run
them with "Apple could not verify ... is free of malware". Clear the flag with
xattr -dr com.apple.quarantine pgrust-0.4-beta-$PLATFORM, or approve each
binary under System Settings > Privacy & Security > "Open Anyway".
Columnar storage (pgrcolumnar) is a table access method. Register it once in
each database, then create tables with USING pgrcolumnar:
CREATE ACCESS METHOD pgrcolumnar TYPE TABLE HANDLER heap_tableam_handler;
CREATE TABLE events (id bigint, kind text, val double precision, at timestamp)
USING pgrcolumnar;- Stop the server with Ctrl-C in its terminal, or
kill -INT <pid>. That is Postgres' fast shutdown and it is safe. - Start it again by rerunning the same server command with the same data directory. Your data is preserved across restarts.
- Start over by deleting the data directory:
rm -rf /tmp/pgrust-data. This deletes all data in it. - If startup fails with
FATAL: lock file "/tmp/.s.PGSQL.5432.lock" already exists, another server, most likely an existing Postgres install, is already using port 5432. Start pgrust on a different port with-p 5433and connect withpsql -h /tmp -p 5433 -U postgres. Whenever a regular Postgres is running on the same machine, checkselect version()after connecting; it is easy to silently connect to the wrong server and wonder why nothing behaves like pgrust. - If startup fails with
could not open directory "/usr/local/pgsql/share/timezone", the server cannot find its share files. It reads them from thesharedirectory beside the binary, so runbin/postgresfrom the unpacked tarball as is, without moving the binary away frombin/share.PGRUST_PGSHAREDIRandPGRUST_TZDIRpoint it elsewhere. - Columnar tables from v0.3 (
USING pgrcolumnar) are refused with an error saying the table is in the v0.3.x columnar format. Dump them with the v0.3 binary (pg_dump) and restore them into v0.4-beta.
We don't recommend running pgrust in production (yet), but we are successfully running it ourselves internally. Keep Postgres as the source of truth for data you can't afford to lose.
pgrust still has a lot of bugs. Our #1 priority right now is testing and reliability.
If you need production ready Postgres today, use Postgres.
PostgreSQL extensions don't work out of the box: there is no stable extension ABI in pgrust yet. But pgrust ships a large number of them, including pgvector, pg_cron, pg_stat_statements, pg_trgm, hstore, pgcrypto, postgres_fdw and most of contrib. PL/Python, PL/Perl and PL/Tcl are not supported yet.
Measured on c8g.4xlarge (AWS Graviton4), pgrust with pgrcolumnar, its
builtin columnar layout, runs ClickBench's analytical queries up to 1,600x
faster than PostgreSQL (per query, cold runs, against PostgreSQL's published
results on the same instance type).
For a code-level walkthrough of one part of that speedup, read how batching, operator fusion, and SIMD speed up the query engine.
- How AI Changes the Economics of JIT Compilers: building a copy-and-patch ARM64 JIT in Rust.
- pgrust: Rebuilding Postgres in Rust with AI: why we started the project.
The most common question we get about pgrust is: how can anyone trust it? Passing a test suite doesn't mean our code is correct. Getting to the point where people can trust pgrust is our number one priority today.
To achieve that goal, we're taking several different approaches:
- Of the 3,400 user facing Postgres functions, we've formally verified
that 1,397 have identical behavior in pgrust with
Kani; 466 more are tested
against Postgres. The ledger is
proofs/USER_FACING_FUNCTIONS.tsv. - We're engaging with Antithesis to do simulation testing of pgrust to battle test pgrust.
- We're doing aggressive differential fuzz testing to ensure pgrust's behavior is identical to Postgres's.
pgrust uses unsafe code, but only for the specific things that need it. Postgres represents every internal value as a Datum, an untyped 8 byte value, and Rust has no safe equivalent, so the code that packs and unpacks internal values is unsafe. The same goes for the places where pgrust must match Postgres' memory layouts byte for byte.
If there are particular unsafes that are not necessary and removing them would not have an impact on performance, let us know, as we would love to remove more unsafes.
You need the Rust toolchain (any rustup install works;
rust-toolchain.toml pins the build to Rust 1.96.0 and rustup fetches it
automatically) and the RE2 regex library:
- Debian/Ubuntu:
sudo apt-get install -y build-essential pkg-config libre2-dev - macOS:
brew install re2 pkg-config
cargo build --release --locked -p main_main -p initdbselect version() names the build: a build in a git checkout prints its short
commit (PostgreSQL 18.6 (pgrust 0.4-beta, build 1a2b3c4) on ...); set
PGRUST_BUILD_ID at build time to choose the id, or build outside git for none.
postgres --version prints pgrust 0.4-beta (PostgreSQL 18.6 compatible);
postgres -V prints postgres (PostgreSQL) 18.6, the line PostgreSQL's own
pg_ctl, initdb and pg_upgrade check.
The server binary lands at target/release/postgres, pgrust's initdb beside
it, and the share tree it needs (postgres.bki, the setup SQL, timezone,
timezonesets, tsearch data, extension files) is staged at target/release/share/. No C
PostgreSQL is involved in creating or running a cluster:
target/release/initdb -D /tmp/pgrust-data --no-locale --encoding UTF8 -U postgres
ulimit -s 65520
RUST_MIN_STACK=33554432 target/release/postgres -D /tmp/pgrust-data \
-k /tmp -p 5432 -c listen_addresses= -c io_method=sync -c max_stack_depth=60000The time zone database is PostgreSQL 18.6's own compiled tzdata, read from
share/timezone beside the binary (never the host zoneinfo, like C built
without --with-system-tzdata). To use the system zoneinfo instead, build with
PGRUST_SYSTEM_TZDATA=/usr/share/zoneinfo (C's --with-system-tzdata);
PGRUST_TZDIR and PGRUST_PGSHAREDIR remain runtime overrides, not
requirements. A data directory made
by stock PostgreSQL 18 initdb still works too (same on-disk format).
Release builds refuse to compile without RE2 on purpose: a
build with only the fallback regex engine is drastically slower on regex
heavy workloads. The binaries we publish additionally use --profile dist,
which turns on fat LTO; it produces a faster binary but takes much longer to
compile.
- Gravity, a new query engine. Gravity runs every query and every kind of statement (DML with ON CONFLICT and RETURNING, triggers, row locks, cursors, PL/pgSQL and SPI, COPY) with PostgreSQL's semantics, on columnar tables and on the ordinary row store alike. It replaces our previous engine and runs analytical queries as much as 3x faster than ClickHouse. Its JIT was rewritten around a new low-level IR: it compiles each plan to AArch64 or x86-64 machine code, and to WebAssembly in the browser build, or interprets it when compiling would not pay off.
- pgrcolumnar grows up.
pgrcolumnarstores compressed, immutable parts with zone maps, delete vectors and background compaction. Reads see PostgreSQL MVCC snapshots, and writes are transactional: they roll back with the transaction. Small inserts land in a row-store front that is folded into columnar parts in the background, and UPDATE and DELETE mark rows in delete vectors instead of rewriting parts (ON CONFLICT and MERGE are not supported on columnar tables yet). Columnar tables take non-unique B-tree, hash, GiST, SP-GiST and GIN indexes, are written to WAL, so they survive crashes and replicate to standbys, and support row locks and asorted_bytable option. - One memory budget covers the buffer pool, the columnar caches and running queries. Near the memory limit pgrust cancels the statement holding the most memory instead of letting the OS kill the server.
- jsonb statistics.
ANALYZEcollects statistics on the keys and values insidejsonbcolumns, and the planner uses them for->,->>,#>,#>>and?,?|,?&, so it estimates jsonb filters instead of guessing. On by default (pgrust.jsonb_path_stats). - The condition cache. Filter results on columnar tables are reused
across statements over the same immutable data.
fullis the default (also spelledon) and stores match counts too.rowskeeps row selections and per-dictionary-code filter tables without counts;granulekeeps only empty-granule verdicts;offdisables predicate reuse.pgrust.dict_filter_cacheandpgrust.zone_storeturn off the per-dictionary-code filter tables and the zone-map result store on their own. Sizing feedback, data caches, and startup prewarm have separate controls. - pgrust's own
initdb, andpg_cron1.6 is included. - Upgrading: columnar tables from v0.3 must be dumped and restored. UTF8 is the only server encoding.
v0.4-beta is a beta: it shows what is coming in v0.4 while we finish stabilizing the rest.
Up to v0.3 we developed pgrust privately and published it in large monthly releases. Starting with v0.4-beta, development happens in this repository: features, optimizations and bug fixes are pushed here as soon as they are ready, instead of being held back for the next release.
Executor
- Gravity, a vectorized, push-based executor that compiles plans to machine code (AArch64 and x86-64) with a JIT that emits code directly.
Parallelism
- Threads instead of processes, for both connections and parallel query.
- A query scheduler, new to Postgres. Queries get a priority, and long-running queries have theirs lowered, so heavy queries interfere less with fast ones.
- Parallel query rebuilt around work stealing: idle threads are assigned dynamically to speed up queries already in flight.
Storage
pgrcolumnar, a column-oriented format with dictionary encoding and several other compression schemes, WAL-logged.- Pipelined
fsync. A worker releases its locks after callingfsyncbut before it completes. This is safe because the query is not acknowledged, and none of its effects are observable, until thefsyncfinishes.
Other
- The automatic memory manager. Near the machine's memory limit, pgrust cancels the statement holding the most memory, so that one query dies and the server itself can keep running.
Next we're working on testing and battle testing pgrust and making it so people can trust pgrust with their data.
Beyond that we want to add:
- Instant forking
- Autoscaling including scale to zero
- Planner support for JSON
- Undo log support for a no-vacuum design
- Adaptive planner to prevent bad plans
- A mini version of pgrust designed for testing and being embedded
We are not actively accepting pull requests right now. If you want to help out with the project, reach out on Discord.
Please do still open an issue if something breaks, if setup is confusing, or if there is a Postgres behavior you want matched.
pgrust is licensed under AGPL-3.0. Portions derived from
PostgreSQL remain under the PostgreSQL License. The JIT's code generator
(crates/tpde/) is ported from TPDE (Apache-2.0 WITH LLVM-exception) and
Fadec and Disarm (BSD-3-Clause). See NOTICE.
Acknowledgements: the research and other systems whose ideas pgrust uses are
credited in ACKNOWLEDGEMENTS.md.
- Discord: https://discord.gg/FZZ4dbdvwU
- Email: maintainers@pgrust.com
- Twitter/X: @pgrustdb
- Updates: https://pgrust.com/#updates