Open-source project
xtdb/xtdb avatar
xtdb/xtdb

XTDB: an immutable SQL database where history is the default query

An immutable SQL database for application development, time-travel reporting and data compliance. Developed by @juxt

3,086 stars196 forksClojureMPL-2.0

At a glance

What is it?
A Clojure database that appends instead of updating, keeps two clocks on every row, and answers questions about the past as a normal SQL query rather than an audit feature.
Who is it for?
XTDB earns a look when your application has a question that ordinary tables answer badly: what did this record look like on a given day, what did we believe about a customer in March, or which rows changed without anyone touching them. The bitemporal model handles those directly, the log-as-coordinator architecture is the reason it can, and the Postgres wire protocol means your existing SQL tooling is not stranded.
Can I use it commercially?
Yes, with conditions. MPL-2.0 is a weak copyleft licence: you can use it inside commercial and closed-source software, but if you distribute changes to its own files, you must publish those changes under the same licence.
Is it still maintained?
Yes. The repository received new commits within the last day.
What is it written in?
Mainly Clojure, according to GitHub's language statistics.

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

From daily snapshots

Star history

  • xtdb/xtdb3,086 ★ · +7
Star history
Repository2026-10-072026-10-23
xtdb/xtdb3.1k3.1k

7 Stars in the last 16 days, measured from the daily GitHub snapshot. Compare with other projects

Editorial analysis

Immutability here means append, not history tables

Most databases keep history as an option. You enable a temporal table, or you write an audit trigger, or you add an audit log application side, and the current state lives in a mutable table with a shadow record of what used to be true. XTDB inverts that. The README's first feature is immutability: the engine is optimised for current-time queries, but you can audit the full history of the database at any point, without needing snapshots or accessing backups.

The practical difference is that `UPDATE` and `DELETE` are not operations you plan around. A write puts a new version of a row into the system and marks the previous one superseded as of that moment. Reading the current state is a query that skips the superseded versions, and reading a past state is the same query with a timestamp. There is no second mechanism to keep correct, which is where most audit implementations quietly rot.

This matters more for compliance and financial data than it does for a content site. When an auditor asks what a customer's address was on a specific date, the answer is a query rather than an export from a backup taken on some Tuesday. The README states the motivation directly: simplify application development and address complex data compliance requirements. That is a narrow goal, and it is worth judging the project against that goal rather than against general-purpose database benchmarks.

Two clocks: system time and valid time

The second feature is the one that earns the project its repository topics. XTDB calls itself bitemporal, and the README splits that into two kinds of time that are tracked separately.

System time is when the database learned something. Every write gets one automatically. Valid time is when that information is true in your business domain, and you supply it yourself. The README's phrasing is that data is versioned as updates are made in system time, while you can separately record and query when the data is, was, or will become valid in your domain.

The distinction earns its keep on retroactive facts. Suppose a support agent discovers on 1 June that a subscription lapsed on 20 May, and you write that today. System time is 1 June because the database learned it then. Valid time is 20 May because that is when the lapse happened. A report asking what the system believed on 25 May should show the subscription as active, because on 25 May the database did not know better. An ordinary mutable database cannot express that question at all, which is why teams end up building correction tables by hand.

The `FOR VALID_TIME` and `FOR SYSTEM_TIME` clauses are how you ask both questions, and both are part of XTQL rather than an extension bolted on afterwards.

XTQL, SQL:2011 and the Postgres wire protocol

XTDB speaks two query languages, which is more of a feature than it first sounds. It accepts SQL and XTQL, its own language, and the README describes XTQL as a full SQL dialect implementing the bitemporal functionality as specified in the SQL:2011 standard. The standard's temporal tables proposal is the reference point, so the semantics are written down in a document you can read rather than only in this project's source.

Both are available over the Postgres wire protocol. That is the detail that decides whether XTDB is usable in your stack today. A Postgres wire endpoint means psql connects, BI tools that speak the protocol connect, and migration libraries and ORMs written for Postgres have a chance of working. You are not restricted to a bespoke client. The cost is that a tool expecting Postgres semantics may not understand temporal clauses, so the compatibility is at the connection layer rather than at the query language layer.

The project also documents itself separately from the main README. The `docs/` directory holds a documentation site, and v1 users are pointed at a separate documentation site on the `1.x` branch. A Zotero bibliography group is listed as well, which tells you the temporal database literature is being tracked rather than reinvented.

Inside-out architecture and the log as coordinator

The README has a section titled Inside-out Architecture, and it establishes that XTDB embraces a log as the central point of coordination when running as a distributed system. The claim it quotes, from Martin Kleppmann, is that turning the database inside out brings simpler code, better scalability, lower latency and more flexibility for doing interesting things with data.

What that means concretely is that nodes coordinate through an append-only log rather than through each other. There is no separate consensus layer, no leader election to reason about, and no separate transaction coordinator. The log is the transaction coordinator. For a bitemporal store this is a natural fit rather than a fashion choice, because the log's entries are exactly the version records the query layer needs. Writing and time travel end up being two views of the same structure.

The architecture diagram lives at `docs/public/images/docs/xtdb-node-1.svg` and is labelled as the XTDB 2.x architecture diagram, which is a useful hint that the distributed design differs from what 1.x did. The storage layer follows from the same thinking. The engine is described as ACID and columnar, built on Apache Arrow and designed for object storage, so history is stored as data files rather than as mutable page structures. A `sqlite-test/` directory in the tree suggests a lighter local mode for testing, and `modules/`, `core/` and `main/` are the layering of a codebase that is largely Clojure.

The `docker-compose.yml` in the repository shows what a developer run actually pulls: Kafka in KRaft mode as the log, MinIO for object storage with a setup job that creates the `xtdb` bucket, and Keycloak on port 8080 for auth. Three infrastructure dependencies before your first row is a fair signal of the distributed posture.

Dynamic documents without a schema up front

The third feature is dynamic: you do not need to specify schema up-front before documents, described as rows with arbitrarily nested data, can be inserted. For an application whose entity shapes differ, or whose payloads vary by client version, that removes a migration step that would otherwise run ahead of the code that needs it.

Dynamic here is not the same as untyped, and the difference is worth being precise about. The engine still needs to know about a row once it is inserted, which is why a columnar store on Arrow needs column information at write time. What you avoid is the separate act of declaring the schema before inserting. The trade is that queries against fields that do not exist everywhere return uneven results, so the flexibility moves the burden onto your query writing rather than removing it.

The build is Gradle and Kotlin-DSL based, with `build.gradle.kts`, `settings.gradle.kts`, `buildSrc/` and `build-logic/` at the root of a repository whose primary language is listed as Clojure. The presence of `dokka/` for API documentation, `.clj-kondo/` for Clojure linting, `.lsp/` and `.mcp.json` for editor and tool integration, and `AGENTS.md` and `CLAUDE.md` at the root all describe a project that has been developed with modern tooling conventions rather than an abandoned experiment.

Who builds it, how it is licensed, and where it stands

XTDB is written and supported by JUXT, and the repository carries a copyright line reading Copyright 2018-2026 JUXT LTD. That is a company behind the project, not a foundation or a solo maintainer, which is a different risk profile from most infrastructure in the Clojure ecosystem. Commercial backing means someone is paid to keep the release train moving, and it also means the roadmap is ultimately one company's. The README gives [email protected] as the contact and points to a Discord community and GitHub Discussions for support.

Licensing is MPL-2.0, at your option any later version. MPL is file-level copyleft, so modifications to the project's own files must be published, while linking it into a larger application does not force that application open. For a commercial product embedding XTDB as a service, that is a workable arrangement, and it is worth noting that JUXT also offers commercial support separately from the open source build.

The release line is where an adopter needs to be careful. The recent tags are `v2.2.0-rc0` on 2026-07-21, `v2.2.0-rc1` on 2026-08-06 and `v2.2.0-beta2` on 2026-09-17, which puts the last push to the default `main` branch on 2026-09-23, days after that beta. So the 2.x line is still pre-release. That is normal for a project mid-rewrite and not a criticism of it, but it means you should decide deliberately between the 1.x branch, which has its own documentation site and a more settled history, and the 2.x line, which has the architecture described in the current README.

With about 3,000 stars and a small number of open issues against that, the project is established enough to be taken seriously and young enough that its API surface is still moving. Artifacts publish to Maven Central under `com/xtdb/`, so consumption is through the JVM ecosystem rather than a bespoke client library.

When a normal relational database is the better answer

The clearest case against is a workload with no historical question to ask. If your application updates a row and only the current value is ever read, immutability buys nothing and costs storage, since every version of every row stays in the system and nothing is reclaimed by an update. For an OLTP workload on a hot table, that is a real cost with no matching benefit.

The second case is operational. XTDB's distributed posture means Kafka, object storage and, if you use the compose file as written, Keycloak. That is more to run and more to understand than a Postgres instance. A team that wants temporal queries on a modest dataset can get them in Postgres with a versioning column and a view, which is less elegant but has a much smaller bill.

The third is maturity, and it is the honest one. A pre-release 2.2 line, a v1 documentation site maintained separately, and a Java and Clojure dependency tree you will pull from Maven Central all describe a project where the operational documentation you find online may describe a version you are not running. Check that the deployment guide matches the branch you are on.

Where XTDB is hard to replace is a product whose users can legitimately ask when the system knew something, what it thought at the time, and whether a record has been altered. For that product, the alternatives are an audit system you build and maintain yourself, and a data warehouse you query in parallel with the operational store.

Editorial conclusion

XTDB earns a look when your application has a question that ordinary tables answer badly: what did this record look like on a given day, what did we believe about a customer in March, or which rows changed without anyone touching them. The bitemporal model handles those directly, the log-as-coordinator architecture is the reason it can, and the Postgres wire protocol means your existing SQL tooling is not stranded. Two things to check before committing. First, the release channel: `v2.2.0-rc0`, `v2.2.0-rc1` and `v2.2.0-beta2` are all from the last two months of the 2.x line, so 2.x has not settled into a stable tag yet, and the README points v1 users at a separate `1.x` documentation site. Second, the vendor: this is built and supported by JUXT, a company, under MPL-2.0, so the questions to ask are about JUXT's roadmap and commercial support rather than about an abandoned community project. If you take the bet, start from the `dev` branch guide on the `main` branch rather than from the v1 docs, because the architecture moved when the log became the coordinator.

Frequently asked questions

What is a bitemporal database?

One that tracks two separate timelines per record. System time says when the database learned the value, and valid time says when the value is true in the business domain. That lets you ask both what the system currently believes and what it believed on a past date, which a single-clock history cannot answer.

Is XTDB written in Clojure and what license does it use?

The primary language is Clojure, with a Gradle Kotlin-DSL build and Javadoc or Dokka API docs. The license is MPL-2.0, or at your option any later version, which is file-level copyleft: linking XTDB into a closed application does not force that application open.

Can I query XTDB with standard SQL clients?

XTDB exposes both SQL and XTQL, its own dialect implementing the bitemporal behaviour from SQL:2011, over the Postgres wire protocol. Tools that speak that protocol, including psql and many BI clients, can connect, though they will not understand the temporal clauses unless they are XTDB aware.

How do I run XTDB locally for development?

The repository's docker-compose.yml starts Kafka in KRaft mode as the log, MinIO for object storage with a setup job that creates the xtdb bucket, and Keycloak on port 8080. There is also a sqlite-test directory in the tree, which suggests a lighter local mode for testing without that stack.

Who maintains XTDB?

JUXT, a company, writes and supports XTDB. The repository carries a copyright line for JUXT LTD, the README points to [email protected], Discord and GitHub Discussions for support, and artifacts publish to Maven Central under com/xtdb. Commercial support is offered separately from the open source build.

Official sources

  1. License: MPL-2.0
  2. Project website
  3. README
  4. Releases
  5. xtdb/xtdb on GitHub
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/xtdb-xtdb.svg)](https://hysenlabs.com/projects/xtdb-xtdb)

Star history chart for your README

markdown
[![Star history](https://hysenlabs.com/chart/xtdb-xtdb.svg)](https://hysenlabs.com/projects/xtdb-xtdb)

Was this useful?