Self-hosted service
Evokoa/pgGraph avatar
Evokoa/pgGraph

pgGraph: graph traversal as a PostgreSQL extension, not a second database

Open-source graph database superpowers for your existing Postgres data.

1,044 stars84 forksRustNOASSERTION

At a glance

What is it?
pgGraph keeps your tables as the source of truth and adds a derived graph index queried through the `graph` schema. The design is sound for teams already on Postgres, but the licence file is ambiguous and the README leaves index maintenance under-specified.
Who is it for?
pgGraph fits teams that already run PostgreSQL 14 to 18, have foreign keys or join tables describing relationships, and want hop-limited traversal or shortest path queries without operating a second datastore.
Can I use it commercially?
Check first. The repository uses a licence we do not classify automatically, so read its LICENSE file before any commercial use.
Is it still maintained?
Yes. The repository last received commits 25 days ago.
What is it written in?
Mainly Rust, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on September 15, 2026, and from our analysis. They are not legal advice.

DEEP OPEN-SOURCE ANALYSIS

The recursive SQL tax pgGraph is trying to remove

The problem pgGraph targets is specific. Relational schemas express relationships through foreign keys, but questions like "find records related to Alice within two hops" or "find the shortest path between this person and this company" do not map cleanly onto a single join. The README frames this as the reason the extension exists: graph-style questions "often require custom recursive SQL for each schema." Every new question means another hand-written recursive CTE, tested against production data volumes, tuned by hand. The audience is therefore teams that already have their data in PostgreSQL, already have the relationships encoded as foreign keys or join tables, and do not want to stand up a separate graph database with its own storage, query language and operational surface. pgGraph's pitch is that your tables stay the source of truth and the graph is derived. That is a narrower claim than "we are a graph database", and it is the more defensible one.

A derived index queried through the graph schema

The mechanism described in the README is a derived graph index, not a rewrite of your storage. Your ordinary tables remain authoritative. pgGraph reads the relationships it can discover, builds an index from them, and exposes query functions in the `graph` schema so you call them from SQL rather than from a new query language. The quickstart demo illustrates the intended flow: it creates two normal PostgreSQL tables, discovers the foreign key relationship between them, builds the graph, then runs example queries. Discovery is the part worth noting. You are not hand-writing an edge list in a separate format; the extension inspects existing constraints. The playground modes hint at an implementation choice the README does not explain. `scripts/quickstart.sh playground panama csr` and the same command with `mutable` suggest two index representations, one likely a compressed or static structure and one supporting in-place change. Which one you get by default, and what the trade-off between them is, is not stated in the supplied material. That is a gap, because the choice determines how the index behaves under writes.

Installing it: Docker, Homebrew, or pgrx

Three installation paths appear in the README. The quickstart clones the repository and runs `scripts/quickstart.sh`, which builds a disposable PostgreSQL 17 image. You can confirm the extensions loaded without a local client by running `docker exec pggraph psql -U postgres -d graph -c "SELECT extname, extversion FROM pg_extension WHERE extname IN ('graph', 'pg_cron');"`. The Homebrew path uses the Evokoa tap: `brew tap Evokoa/tap`, `brew install pggraph`, `brew test pggraph`, then `brew services start postgresql@17` and `psql -d postgres -c "CREATE EXTENSION graph;"`. The source path is `scripts/quickstart.sh pgrx`, which per the README runs `cargo pgrx install` against a local PostgreSQL. Beyond the default demo mode, the script accepts `setup`, `psql`, `docker CONTAINER [PG_MAJOR] [DB_NAME] [DB_USER]`, `pgrx [PG_MAJOR]`, and `playground [panama|ldbc] [csr|mutable]`. The `docker` mode delegates to `scripts/install_into_docker_postgres.sh` to install into an already running container. Note the dependency list: Docker or Docker Desktop for the container paths, WSL2 on Windows. The release image is `ghcr.io/evokoa/pggraph:1.2.0`, and the README asks you to verify its published digest before deployment. Do that. A tag is mutable; a digest is not.

Where the derived index becomes the problem

The design has a structural cost. Because the graph is derived rather than authoritative, every write that changes a relationship has to be reflected in the index, and the README does not describe the refresh strategy, the staleness window, or how a bulk update is handled. The quickstart notes one concrete symptom: if the image's scheduled maintenance is already active, the demo waits for that bounded operation to finish before building rather than failing on a transient lock. That is a courtesy in a demo script. In production it is a signal that index builds contend with background maintenance, and it tells you nothing about what happens when your own workload holds locks during a rebuild. The second limitation is scope. pgGraph discovers relationships, which in practice means constraints it can read. If your edges live in a polymorphic association table, a JSON column, or an external system, the discovery step has nothing to work with and you are back to writing the mapping yourself. The third is expressiveness. Traversal and shortest path are graph questions, but they are not all graph questions. There is no mention of pattern matching, no mention of graph algorithms beyond path finding, and no Cypher-compatible surface. If you need those, this is the wrong tool and the README does not pretend otherwise.

pgGraph versus Apache AGE and recursive CTEs

The obvious comparison is Apache AGE, which also runs inside PostgreSQL and also avoids a second datastore. The difference is in approach. AGE implements openCypher on top of Postgres, which means you write graph queries in a graph query language and the extension translates them. pgGraph keeps you in SQL and exposes functions in the `graph` schema, so the query you write is a SQL query that calls graph primitives. That matters for two reasons. First, existing SQL tooling, ORMs and migrations keep working with less friction when the surface is still SQL. Second, you give up Cypher's pattern-matching syntax, which is genuinely more compact for expressing multi-hop patterns with filters on both nodes and edges. The other alternative is doing nothing: writing recursive CTEs by hand. That is not a strawman. For a single fixed question on a stable schema, a recursive CTE is a few dozen lines, has no extension to install, no index to keep fresh, and no upgrade path to track. pgGraph earns its place when the number of graph questions grows past the point where maintaining a library of recursive queries is reasonable. Below that threshold, it is added operational surface for little gain.

Release cadence, pgrx coupling and the licence problem

Three releases landed in roughly a month: v1.0.0 on 2026-07-25, v1.1.0 on 2026-08-16, v1.2.0 on 2026-08-22. The v1.1.0 title references caller-scoped row-level security and safe replacement, and v1.2.0 references open-vocabulary relationship types. Both are meaningful changes to query semantics and to what counts as a relationship, which means an upgrade is not purely mechanical. The extension is written in Rust and built with pgrx, so your upgrade path is tied to pgrx's own compatibility with your target PostgreSQL major version. The README states support for PostgreSQL 14 through 18. Before upgrading a cluster, check that pgrx supports the same pairing, because the extension will not build otherwise. The licence is the item to resolve first. The repository is classified as NOASSERTION, while the README badge displays Apache-2.0 and the README links to a LICENSE file. Those two signals conflict, and the discrepancy is not explained in the supplied material. A permissive licence and an unresolved one are different propositions for anyone embedding this in a product. Read the LICENSE file directly and, if the terms matter to your distribution, get them reviewed. Nothing here is legal advice; the point is that the classification is not self-explanatory.

What to check before you point pgGraph at a real schema

The managed offering at polygres.com is mentioned in the README as the high performance path for GraphRAG on Postgres, which means the self-hosted extension is not necessarily the configuration the vendor optimises for. That is worth knowing before you benchmark your own deployment and compare it to published numbers, because the supplied material gives no performance figures for either. The practical checks are narrower. Confirm the digest of `ghcr.io/evokoa/pggraph:1.2.0` before pulling it. Run the quickstart demo against a copy of your schema rather than the sample tables, and watch whether foreign key discovery finds the relationships you expect; if it does not, the index will be built from an incomplete edge set and every traversal result will be quietly wrong rather than erroring. Test a bulk update against the index and observe whether queries return stale results, since the README does not state a refresh model. If you use row-level security, re-test after the v1.1.0 caller-scoped changes, because RLS behaviour on a derived index is exactly the kind of thing that changes between minor versions. And decide which playground mode matches your workload, `csr` or `mutable`, before you assume the default is the right one.

Editorial conclusion

pgGraph fits teams that already run PostgreSQL 14 to 18, have foreign keys or join tables describing relationships, and want hop-limited traversal or shortest path queries without operating a second datastore. It does not fit teams whose graph workloads are write-heavy or whose relationships are not expressible as foreign keys, and it is not a replacement for a dedicated graph engine when you need openCypher, pattern matching across arbitrary node types, or graph algorithms beyond traversal and shortest path. Before adopting, verify three things yourself: the actual licence terms behind the NOASSERTION classification, the rebuild and refresh behaviour of the derived index under your write volume, and whether row-level security behaves as you expect after the caller-scoped changes in v1.1.0. Confirm the release image digest at ghcr.io/evokoa/pggraph:1.2.0 rather than trusting the tag.

Official sources

  1. Evokoa/pgGraph on GitHub
  2. Issues
  3. Project website
  4. README
  5. Releases
Community notes

Community notes