Apache Polaris

Apache Polaris is an open-source REST catalog for Apache Iceberg, originally built and open-sourced by Snowflake in 2024 and subsequently donated to the Apache Software Foundation. It implements the Iceberg REST Catalog specification and is rapidly becoming the standard answer to “what catalog do I run for Iceberg?” alongside AWS Glue and Nessie.

Key Features:

Architecture:

Polaris exposes the Iceberg REST API over HTTP, backed by a relational metastore (Postgres / managed Snowflake) for catalog state. Engines authenticate, list namespaces, and request table metadata pointers; Polaris then vends temporary credentials scoped to the requested table’s storage location. The actual data reads/writes happen directly between the engine and object storage.

Use Cases:

4. Where Polaris Fits

Polaris in an Iceberg Lakehouse Spark REST catalog client Trino REST catalog client Snowflake external catalog Flink REST catalog client Apache Polaris Iceberg REST API, RBAC, credential vending Metastore DB catalog state Object storage S3 / GCS / ADLS: data + metadata

An Iceberg table is a set of data files plus a tree of metadata files on object storage. What the files alone cannot provide is a single, atomically updated answer to "which metadata file is the current version of this table?" That is the catalog's job. Polaris implements the Iceberg REST Catalog specification, so any engine with a REST catalog client can list namespaces, load tables, and commit changes through it. Because every engine goes through the same catalog, a table written by Spark is immediately visible to Trino or Snowflake with consistent snapshots and a single place to enforce permissions.

5. How a Commit Works

Commit and Read Flow loadTable engine asks catalog Metadata pointer current metadata.json Vended creds scoped, short-lived Read / write files object storage commitTable atomic pointer swap

When an engine loads a table, Polaris returns the location of the current metadata file and, if credential vending is configured, temporary storage credentials scoped to that table's location. The engine reads or writes data and manifest files directly on object storage; Polaris is not in the data path. To commit, the engine sends the new metadata along with requirements such as "the current snapshot is still X". Polaris applies the change only if the requirements hold, giving optimistic concurrency: one of two conflicting writers succeeds and the other retries against the new state.

6. Access Control Model

Polaris separates principals, principal roles, and catalog roles. Privileges such as TABLE_READ_DATA or NAMESPACE_CREATE are granted to catalog roles on a catalog, namespace, or table; catalog roles are granted to principal roles; principal roles are assigned to principals, which are the service identities that engines authenticate as. This indirection lets one engine identity have different rights in different catalogs. Combined with credential vending, engines never need broad standing access to the storage bucket.

7. Worked Example: Spark Configuration

from pyspark.sql import SparkSession

spark = (SparkSession.builder
  .config("spark.sql.extensions",
          "org.apache.iceberg.spark.extensions.IcebergSparkSessionExtensions")
  .config("spark.sql.catalog.polaris", "org.apache.iceberg.spark.SparkCatalog")
  .config("spark.sql.catalog.polaris.type", "rest")
  .config("spark.sql.catalog.polaris.uri", "https://polaris.example.com/api/catalog")
  .config("spark.sql.catalog.polaris.warehouse", "analytics")      # catalog name
  .config("spark.sql.catalog.polaris.credential", "CLIENT_ID:CLIENT_SECRET")
  .config("spark.sql.catalog.polaris.scope", "PRINCIPAL_ROLE:ALL")
  .config("spark.sql.catalog.polaris.header.X-Iceberg-Access-Delegation",
          "vended-credentials")
  .getOrCreate())

spark.sql("CREATE NAMESPACE IF NOT EXISTS polaris.sales")
spark.sql("""CREATE TABLE IF NOT EXISTS polaris.sales.orders
             (id BIGINT, amount DECIMAL(12,2), ts TIMESTAMP)
             USING iceberg PARTITIONED BY (days(ts))""")
spark.sql("SELECT count(*) FROM polaris.sales.orders").show()

The Iceberg Spark runtime jar matching your Spark version must be on the classpath. Keep the client secret in a secret store rather than in code.

8. Polaris vs Other Catalogs

CatalogInterfaceNotes
Apache PolarisIceberg RESTVendor-neutral, RBAC, credential vending
Unity CatalogOwn API plus Iceberg RESTDelta-first, Databricks ecosystem
AWS Glue Data CatalogGlue API, Iceberg REST endpointAWS-native, IAM permissions
Hive MetastoreThriftLegacy, no fine-grained Iceberg features
NessieOwn API plus Iceberg RESTGit-like branches across tables

9. Pitfalls