
Project Nessie
An open-source catalog for Apache Iceberg with Git-like branches, tags and commits that span every table at once, so a set of changes to many tables can be tested on a branch and then merged in one atomic step.
- What it is
- Lakehouse catalogs
- Used for
- Lakehouses
- Type
- Open source
- Made by
- Project Nessie community (started at Dremio)
- License
- Apache 2.0
- Facts checked
- 10 October 2026
What it is
Nessie is a Java server, built on Quarkus, that listens on port 19120. It records every change to a table, view or namespace as a commit, and each commit captures the state of all tables at that moment. A branch is a named pointer that moves with each new commit; a tag is a pointer that stays put. Creating a branch copies no data: Nessie versions the lists of files that make up each table, not the files. One commit can change many tables, and merging a branch replays all of its commits onto the target, so people reading main see a whole job’s changes appear at once or not at all. Conflicts are checked per table: if both branches changed the same table, a normal merge fails. Clients reach Nessie in two ways. The original Nessie API (/api/v2) is used by Iceberg’s NessieCatalog, bundled in Iceberg’s Spark and Flink runtimes since Iceberg 0.11.0, and Spark SQL extensions add commands such as CREATE BRANCH and MERGE. Since 0.90 (June 2024) Nessie also speaks the Iceberg REST protocol at /iceberg: the branch goes at the end of the URI (/iceberg/dev), and a table name can carry a branch, commit or timestamp, as in orders@dev or orders#2024-07-01T00:00:00Z. Nessie’s own guide still calls Iceberg REST support experimental.
Through Iceberg REST, Nessie writes table metadata itself and keeps the storage keys. By default it signs each S3 request for the client (S3 request signing). If you enable assume-role, it vends STS credentials scoped to the table’s location instead, which is faster and is required for Trino, which can’t use request signing. GCS and ADLS work but are marked experimental. The commit history lives in a version store: in memory by default, which loses everything on restart; PostgreSQL, MongoDB, Google Bigtable or single-node RocksDB for production; MariaDB, MySQL, CockroachDB and Cassandra as experimental options; DynamoDB in beta. Authentication (OpenID Connect bearer tokens) is off by default, and authorization rules are CEL expressions over roles, branches and table paths. Old commits keep old files alive, so cleanup has to know about every branch: a separate Nessie GC tool marks the files any live commit still uses, according to a cutoff (a number of commits, an age or a timestamp), and deletes the rest.
Dremio’s CTO Jacques Nadeau announced Nessie in October 2020 as an Apache-licensed project; it was never an Apache Software Foundation project. In 2024 Dremio said it would bring Nessie’s features into Apache Polaris and retire Nessie. That merge hasn’t happened. Dremio moved its own built-in catalog to Polaris in April 2025 and, in September 2026, wrote that its platform is built on Polaris, not Nessie, and that Nessie’s development has slowed considerably. Nessie is still maintained and not archived: 0.109.0 shipped on 8 October 2026, and versions are still 0.x.
Why teams pick it
- Branches and tags over the whole catalog: change dozens of tables on a branch, check them, then publish them all at once with a merge; tags and commit IDs pin a consistent state of every table for reproducible reports and models
- Multi-table transactions are built in: one commit can change many tables, and the API offers optimistic read committed, repeatable read and serializable isolation, though most engines use only the first two
- Two ways in: any Iceberg REST client (PyIceberg, Trino and others) or the older Nessie catalog that ships inside Iceberg’s Spark and Flink runtimes, plus SQL branch commands in Spark and a Java CLI
- A wide choice of databases for its history, from PostgreSQL to MongoDB and Bigtable, and clients never get the bucket keys: Nessie signs S3 requests or vends scoped STS credentials
Watch out for
- Uncertain future: Dremio said in October 2024 it would merge Nessie into Polaris and retire it, has since built its platform on Polaris, and wrote in September 2026 that three people wrote the human-authored commits in Nessie’s last hundred. It is not an ASF project and is still 0.x after six years
- Iceberg REST support is still labeled experimental, as are GCS and ADLS; Trino needs vended credentials, and DynamoDB isn’t recommended with Iceberg REST because of its row-size limit
- Merges work per table: if the same table changed on both branches, a normal merge fails (or
FORCEoverwrites it), with no row-level merging. Only Iceberg tables and views are supported; Delta Lake support was removed in 2023 - Cleanup is your job: with many versions of each table live across branches, the docs say you must rely on Nessie GC, a separate tool you run regularly, with its own PostgreSQL, MariaDB or MySQL database for live sets
- Defaults are for experiments: the in-memory version store loses data on restart, and authentication and authorization are both off
Alternatives
What all lakehouse catalogs have in common →
Unity CatalogCommercialDatabricks’ catalog and governance layer for tables, files, functions and models, with managed and external tables, an Iceberg REST endpoint and credential vending; a separate Apache 2.0 open-source server lives at LF AI & Data.How it fits with other technologies
What Project Nessie uses
- Project Nessieversions tables and views ofApache Iceberg
- Project Nessieadds branch and merge SQL commands toApache Spark
- Project Nessieserves Iceberg tables toApache Flink
- Project Nessieserves Iceberg tables toTrino
- Project Nessieserves Iceberg tables toPresto (PrestoDB)
- Project Nessieis a lakehouse catalog source inDremio
- Project Nessiesigns requests or vends STS credentials forAmazon S3
- Project Nessiecan keep tables in (experimental)Google Cloud Storage
- Project Nessiecan keep tables in ADLS on (experimental)Azure Blob Storage
- Project Nessiekeeps its commit history inPostgreSQL
- Project Nessiecan keep its commit history inMongoDB
- Project Nessiecan keep its commit history, on one node, inRocksDB
- Project Nessiecan keep its commit history (beta) inAmazon DynamoDB
- Project Nessiecan keep commits (experimental) inMySQL
- Project Nessiecan keep commits (experimental) inApache Cassandra
- Project Nessiecan read storage credentials fromHashiCorp Vault
- Project Nessieis written inJava
- Project Nessieis deployed with a Helm chart onKubernetes
Branch the catalog, write an Iceberg table on the branch with PyIceberg, then merge it into main · python
# Nessie 0.109.0 and MinIO (as S3) in Docker, on one network:
# docker network create lake
# docker run -d --name minio --network lake -p 19000:9000 minio/minio server /data
# docker exec minio sh -c 'mc alias set local http://localhost:9000 minioadmin minioadmin && mc mb local/lake'
# docker run -d --name nessie --network lake -p 19120:19120 \
# -e nessie.catalog.default-warehouse=lake \
# -e nessie.catalog.warehouses.lake.location=s3://lake/ \
# -e nessie.catalog.service.s3.default-options.endpoint=http://minio:9000/ \
# -e nessie.catalog.service.s3.default-options.external-endpoint=http://localhost:19000/ \
# -e nessie.catalog.service.s3.default-options.path-style-access=true \
# -e nessie.catalog.service.s3.default-options.region=us-east-1 \
# -e nessie.catalog.service.s3.default-options.access-key=urn:nessie-secret:quarkus:minio \
# -e minio.name=minioadmin -e minio.secret=minioadmin \
# ghcr.io/projectnessie/nessie:0.109.0
# pip install "pyiceberg[pyarrow,s3fs]" (pyiceberg 0.12)
import json, re, urllib.request
import pyarrow as pa
from pyiceberg.catalog.rest import RestCatalog
NESSIE = "http://localhost:19120"
def api(method, path, body=None): # Nessie's own REST API (v2)
req = urllib.request.Request(NESSIE + "/api/v2" + path, method=method,
data=json.dumps(body).encode() if body else None,
headers={"Content-Type": "application/json"})
with urllib.request.urlopen(req) as r:
return json.load(r)
def catalog(branch): # Iceberg REST: the branch goes at the end of the URI
return RestCatalog(branch, uri=f"{NESSIE}/iceberg/{branch}", **{
# ask Nessie to sign each S3 request instead of handing out keys
"header.X-Iceberg-Access-Delegation": "remote-signing"})
# 1. A namespace on main
main = catalog("main")
main.create_namespace("sales")
# 2. Branch the whole catalog: one pointer, no data copied
head = api("GET", "/trees/main")["reference"]
dev = api("POST", "/trees?name=dev&type=BRANCH", head)["reference"]
print(dev["name"], dev["hash"] == head["hash"]) # dev True
# 3. Create and fill a table on dev only
devcat = catalog("dev")
orders = devcat.create_table("sales.orders",
pa.schema([("id", pa.int64()), ("amount", pa.float64())]))
orders.append(pa.table({"id": [1, 2, 3], "amount": [9.5, 20.0, 4.25]}))
print("s3.access-key-id" in orders.io.properties) # False: Nessie signed each request
print(devcat.list_tables("sales")) # [('sales', 'orders')]
print(main.list_tables("sales")) # []: main is unchanged
# 4. Merge dev into main: every change on dev lands in one atomic step
main_ref = api("GET", "/trees/main")["reference"]
dev_ref = api("GET", "/trees/dev")["reference"]
merge = api("POST", f"/trees/main@{main_ref['hash']}/history/merge",
{"fromRefName": "dev", "fromHash": dev_ref["hash"]})
print(merge["wasSuccessful"]) # True
rows = main.load_table("sales.orders").scan().to_arrow()
print(rows.num_rows, sum(rows["amount"].to_pylist())) # 3 33.75
def log(ref): # newest first, hashes cut to 8 characters
return [re.sub(r"([0-9a-f]{8})[0-9a-f]{56}", r"\1", e["commitMeta"]["message"])
for e in api("GET", f"/trees/{ref}/history")["logEntries"]]
print(log("dev"))
print(log("main"))
# ['Update ICEBERG_TABLE sales.orders', 'Create ICEBERG_TABLE sales.orders', 'update namespace sales']
# ['Merged dev at 1b6cc363 into main at 6cce0539', 'update namespace sales']Where Nessie keeps its commit history#
The version store holds commits, branches and tags, not table data. Status as given in the Nessie 0.109 configuration reference.
| Database | Status | `nessie.version.store.type` | Notes |
|---|---|---|---|
| In memory | Development and tests only | IN_MEMORY (default) | Everything is lost on shutdown |
| PostgreSQL | Production | JDBC2 | JDBC is deprecated |
| MongoDB | Production | MONGODB2 | MONGODB is deprecated |
| Google Bigtable | Production | BIGTABLE | |
| RocksDB | Production, single node only | ROCKSDB | |
| MariaDB and MySQL | Experimental | JDBC2 | MySQL works through the MariaDB driver |
| CockroachDB | Experimental, known issues | JDBC2 | “Write too old” errors under contention |
| Apache Cassandra | Experimental, known issues | CASSANDRA2 | Timeouts can surface as user-facing errors |
| Amazon DynamoDB | Beta, only tested against the simulator | DYNAMODB | Not recommended with Iceberg REST because of its row-size limit |
Nessie and Apache Polaris#
When Snowflake open-sourced Polaris in July 2024, Dremio said it would help bring Nessie’s capabilities into it. The Nessie team wrote in August 2024 that the intent was to contribute catalog-level versioning, Git-like semantics and multi-table transactions to Polaris. In October 2024 Dremio went further: it would merge its catalog into Polaris, “at which time Project Nessie will be retired”, as SiliconANGLE reported, quoting Dremio’s CMO: “We will treat Polaris as our catalog and we will merge Nessie into Polaris.” At that point Dremio’s commercial catalog was still based on Nessie.
What happened instead: Dremio 26.0 (April 2025) shipped a built-in catalog powered by Polaris, and Polaris became a top-level Apache project in February 2026. Dremio, which SAP finished acquiring on 6 July 2026, still lets you connect a Nessie server (0.59.0 or later) as a source. Dremio’s September 2026 catalog guide says “the modern Dremio platform is built on Apache Polaris, not Nessie”, that existing Nessie deployments keep working, and that Nessie still does catalog-level branching better than the alternatives. Nessie itself is not archived and keeps shipping releases, but it is maintained by a small group. For a new catalog, weigh branching against that maintenance outlook.
See it in a real stack
No architecture uses Project Nessie yet. Drop it into the playground and wire it up yourself.Open playground →Where it came from2020–2026
By 2020 data lakes were cheap and easy to start with, but lacked basics that data warehouses had. Jacques Nadeau, Dremio’s co-founder and CTO, wrote that “something as simple as atomic visibility of new files or renaming a column were surprisingly difficult”. Table formats such as Iceberg and Delta Lake had begun to fix that for one table at a time, but nothing gave consistency and transactions across tables. On 1 October 2020 Nadeau announced Project Nessie, an Apache-licensed project built on two ideas: cross-table transactions for a data lake, and a Git-like experience for tables and views. It worked with Iceberg, Delta Lake and Hive, integrated with Spark, and stored its history in DynamoDB first, because most of Dremio’s customers ran on AWS.
Iceberg bundled a Nessie catalog module into its Spark and Flink runtimes from version 0.11.0, and Dremio built Nessie into its own products, including a Nessie source in 2023. In June 2023 release 0.62.0 removed Delta Lake support, leaving Nessie Iceberg-only. 2024 brought a new Java CLI with a SQL-like syntax and MariaDB and MySQL support (0.83, May 2024), then the Iceberg REST protocol in 0.90 (June 2024), with Nessie writing table metadata itself and signing S3 requests so clients no longer needed bucket keys.
A month later Snowflake open-sourced Polaris, and Dremio said Nessie’s capabilities would move into it. In October 2024 Dremio announced it would merge Nessie into Polaris and retire it. Dremio switched its built-in catalog to Polaris in April 2025, and by September 2026 described Nessie’s development as having slowed considerably. The project kept releasing nonetheless: 0.109.0 came out on 8 October 2026.
Why it’s built this way
- Git’s model for data
- Teams needed to make independent changes and combine them safely. Nadeau argued that Git already provides “a reasonable and widely adopted model for exactly that”, so Nessie copied its commits, branches, tags and merges, with tables in place of files.
- Version the catalog, not the data
- Nessie records which files belong to each table at each commit and never copies data, which works because data files are immutable. Keeping transaction management apart from table metadata is what the docs credit for scaling to millions of tables and thousands of commits per second.
- Built for the cloud, with a pluggable store
- Nessie was designed “from day one to run at massive scale in the cloud”. DynamoDB came first because Dremio’s customers used AWS, but the commit algorithm wasn’t tied to it, so PostgreSQL, MongoDB, Bigtable, RocksDB and others followed.
- Optimistic transactions held by the client
- The server keeps no long-lived transaction state. A client pins a commit when it starts and can record what it read; Nessie rejects the commit if any of those tables changed, which gives up to serializable isolation without server-side locks.
Timeline
- 2020
Dremio announces Project Nessie on 1 October, under Apache 2.0
- 2023
0.62.0 removes Delta Lake support in June; Nessie becomes Iceberg-only
- 2024
0.90 adds the Iceberg REST protocol, in beta, in June
- 2024
Dremio says in October it will merge Nessie into Polaris and retire it
- 2025
Dremio 26.0 ships a Polaris-based built-in catalog in April
- 2026
0.109.0 ships on 8 October; Dremio says development has slowed
- Dremio blog: Introducing Project Nessie, Jacques Nadeau (1 October 2020)
- Iceberg docs: Nessie catalog (bundled with the Spark and Flink runtimes since 0.11.0)
- Nessie blog: Introducing Nessie as a Dremio source (June 2023)
- GitHub: Nessie 0.62.0 release, removes Delta Lake (23 June 2023)
- GitHub: Nessie 0.83.2 release, Java CLI and MariaDB/MySQL (May 2024)
- GitHub: Nessie 0.90.2 release, Iceberg REST support in beta (June 2024)
- Nessie blog: Open Source Polaris announcement (August 2024)
- SiliconANGLE: Dremio throws its support to Polaris data catalog (29 October 2024)
- Dremio docs: 26.x release notes (26.0.0, April 2025: built-in catalog powered by Polaris)
- Dremio blog: Choosing an Apache Iceberg catalog (10 September 2026)
- GitHub: Nessie 0.109.0 release (8 October 2026)
Sources23 · facts checked 10 October 2026
- GitHub: projectnessie/nessie README (Docker image on ghcr.io, authentication off by default, Apache 2.0)
- Nessie docs: Introduction (commits, branches, tags; no data copies; storage backends; built at Dremio)
- Nessie docs: About Nessie (commits as multi-table transactions, merges replay commits)
- Nessie docs: Transactions (cross-table transactions, isolation levels, table-level conflicts)
- Nessie docs: Configure Nessie with Iceberg REST (experimental; branch in the URI; signing vs vended credentials; time travel)
- Nessie docs: Server configuration reference (version store types and status, S3 request signing, assume role, secrets managers)
- Nessie docs: Authorization (CEL rules)
- Nessie docs: Nessie GC tool (mark and sweep phases, cutoff policies, databases)
- Nessie docs: Management services (run Nessie GC regularly)
- Nessie docs: Spark SQL extension reference (CREATE BRANCH, MERGE behaviors)
- Nessie docs: Trino with Iceberg REST (no S3 request signing in Trino)
- Nessie docs: Nessie CLI (Iceberg REST and native API)
- Iceberg docs: Nessie catalog
- GitHub: Nessie releases (0.x since 2020)
- GitHub: Nessie 0.109.0 release (8 October 2026; Java 17, Helm chart, GC and CLI images)
- Dremio docs: Nessie source (Dremio 26.x supports Nessie 0.59.0 and later)
- Dremio docs: Open Catalog (built on Apache Polaris)
- Dremio docs: 26.x release notes (built-in catalog powered by Polaris, April 2025)
- Dremio blog: Choosing an Apache Iceberg catalog (Nessie status, 10 September 2026)
- Nessie blog: Open Source Polaris announcement (August 2024)
- SiliconANGLE: Dremio throws its support to Polaris data catalog (29 October 2024)
- Polaris blog: Apache Polaris graduates to top-level project (19 February 2026)
- SAP News: SAP completes Dremio acquisition (6 July 2026)