Which Iceberg Catalog Should You Use: Polaris, Unity or Glue?

Quick answer: Which Iceberg catalog you should use comes down to platform gravity, not marketing. Use Unity Catalog if Databricks is your primary compute, AWS Glue if you are deep in Lake Formation and EMR, and Apache Polaris or Snowflake Horizon Catalog if you genuinely run multiple engines against the same tables. The file format is portable; the catalog is where the lock-in lives, because it owns commit coordination, credential vending and policy enforcement.

Last updated: August 2026

Every open-format pitch follows the same script: put your data in Apache Iceberg, keep it in your own object storage, and you are free. Parquet on S3, an open spec, no vendor holding your data hostage. All true, and all beside the point.


An Iceberg table is not just files. Something has to hold the pointer to the current metadata file and arbitrate concurrent commits so two writers do not clobber each other. That something is the catalog, and whoever runs it decides which engines can write, who gets storage credentials, and whether your masking policies survive the trip. Your Parquet is portable. Your catalog is a migration project.


So the question is not whether to go Iceberg - that argument is over. It is which Iceberg catalog you should use, and the answer turns on four things that rarely reach a feature matrix: where your compute already lives, whether you need more than Iceberg, how deep your governance goes, and who is on the hook at 2am.


Why the catalog, not the file format, is the lock-in


An Iceberg table has three layers: data files, metadata files, and a catalog entry saying which metadata JSON is current. The first two are object storage and genuinely portable. The third is a service with an API and a permissions model. It performs the atomic swap on commit, so it is the concurrency control for every writer, and increasingly it performs credential vending: the client asks for a table and gets back short-lived, scoped storage credentials instead of a broad IAM role. Good security, and the moment your governance becomes catalog-specific.


The Iceberg REST Catalog spec (IRC) was supposed to make this swappable. In practice it defines a surface with a lot of optional behaviour, and every implementation covers a different subset. Two catalogs can both be IRC-compliant and still differ on namespace nesting, accepted table spec versions, delegation headers, and whether a policy-protected table is visible at all.


The seven criteria that actually decide the choice



Which Iceberg catalog should you use: side-by-side


The vendor-neutral read as of August 2026, taken from each vendor's own documentation.


CriterionApache PolarisSnowflake Horizon CatalogDatabricks Unity CatalogAWS Glue Data Catalog
GovernanceApache top-level project since Feb 19, 2026Snowflake product featureDatabricks product featureAWS service
FormatsIceberg, plus non-Iceberg formats such as Delta via Generic TablesIceberg; Delta readable via catalog integrationIceberg and Delta nativelyIceberg plus Hive-era types
IRC endpointNative - Polaris is an IRC implementation/polaris/api/catalog/api/2.1/unity-catalog/iceberg-restglue.<region>.amazonaws.com/iceberg
External writesEngine-agnostic by designGA May 26, 2026, Snowflake-managed tablesManaged Iceberg yes; foreign Iceberg read-onlyYes, per Lake Formation permissions
Credential vendingYes for Iceberg tables; not for Generic TablesYes, X-Iceberg-Access-DelegationYes, but not for tables with row filters or column masksYes, Lake Formation scoped credentials
Policy over IRCExternal authorizers: OPA and Ranger, both betaMasking, tag-based masking and row access policies enforced on readsVending blocked on masked tables; cross-engine ABAC reads in betaLake Formation, IAM or hybrid
Ops burdenYou run it, or take a managed distributionNone - part of the accountNone - part of the workspaceNone - AWS-managed
Cost modelInfrastructure you provision0.5 credit per million API calls, as Cloud ServicesBundled with the platformGlue request pricing

Format support: Iceberg only, or Iceberg plus Delta?


This is the question that quietly eliminates options. Apache Polaris took the broadest position: alongside the Iceberg REST APIs it exposes a Generic Table API, GA since Polaris 1.3.0 in January 2026, which registers non-Iceberg tables under a free-form format string - the docs use delta and csv as the examples. Read the limitation before you plan around it: Polaris does not vend credentials for Generic Tables. They are catalogued pointers, so the engine still needs its own storage access. Releases since have been steady rather than dramatic - Apache Ranger as an external authorizer (beta) in 1.5.0, BigQuery Metastore federation docs in 1.6.0, and 1.7.0 in August 2026.


Unity Catalog handles both formats natively, unsurprising given Databricks owns Delta. The nuance is directional: over the Iceberg REST endpoint, managed Iceberg tables support read and write, but Delta tables with Iceberg reads enabled are read-only, as are foreign Iceberg tables. Bi-format in the platform, read-mostly at the REST boundary.


Snowflake came at it differently. Consuming Delta Shares in Horizon Catalog reached GA on July 21, 2026: create a catalog integration with CATALOG_SOURCE = DELTA_SHARING, attach a catalog-linked database, query the shared tables. It is explicitly read-only - no insert, update or create from Snowflake. Good for consuming a partner's share without a copy pipeline; not a bi-format lakehouse. Glue is Iceberg-and-Hive-lineage: a hard stop if you have Delta to catalogue.


Credential vending is where governance quietly breaks


All four vend short-lived storage credentials. The differentiator is what happens when the table sits under a column mask or a row filter, and the answers diverge enough to change your design.


Databricks documents that Unity Catalog credential vending is not supported for tables with row filters or column masks. That is the right call - once an external engine holds raw storage credentials, a row filter is a suggestion. Views, materialized views and tables shared through Open Sharing are outside vending too. Databricks' answer is a separate path rather than an exception: cross-engine ABAC, in beta, lets an external engine read managed tables with row filters and column masks enforced server-side, with Databricks compute doing the filtering instead of handing over storage credentials. It is read-only - writing requires exempting the principal from the policy - and it wants recent clients, Iceberg-Spark 1.11 and Spark 4.0 or above. Snowflake took a different route to a similar place: tables protected by masking, tag-based masking and row access policies can be read over the Horizon REST API from Spark, with policy applied on the read path. The price lands on the write side, where writing to tables carrying fine-grained policies or tags is unsupported.


On AWS, vending is Lake Formation's job: the engine assumes an IAM role, Lake Formation issues scoped credentials for the table location, and permissions are Lake Formation grants, IAM, or hybrid mode. The design lesson: decide up front whether external engines are trusted compute. If not, Unity's model is the honest one, and serve those workloads a governed view rather than table credentials.


What changed in Snowflake in 2026


Three changes landed this year that moved the Snowflake side of this comparison, and most published comparisons predate all three.



Removing the external volume setup is the underrated one. The first two days of any Snowflake Iceberg project used to go on bucket policies and role trust:


SQL - Snowflake-managed Iceberg table, no external volume required
-- Set the default once, at the database level
ALTER DATABASE analytics SET EXTERNAL_VOLUME = SNOWFLAKE_MANAGED;

CREATE OR REPLACE ICEBERG TABLE analytics.public.orders_iceberg (
    order_id        NUMBER(38,0),
    customer_id     NUMBER(38,0),
    order_ts        TIMESTAMP_NTZ,
    order_status    VARCHAR(32),
    total_amount    NUMBER(18,2),
    line_items      VARIANT
)
  CATALOG         = SNOWFLAKE
  EXTERNAL_VOLUME = SNOWFLAKE_MANAGED;

-- Grants an external Spark job needs to read AND write over the REST API
GRANT USAGE ON DATABASE analytics TO ROLE spark_writer;
GRANT USAGE ON SCHEMA analytics.public TO ROLE spark_writer;
GRANT SELECT, INSERT, UPDATE, DELETE, TRUNCATE
  ON TABLE analytics.public.orders_iceberg TO ROLE spark_writer;

One caveat that decides real designs: Snowflake documents this as available only for accounts hosted on AWS or Azure, and not in government regions or in the People's Republic of China. On a GCP-hosted account you are still on external volumes, and this section is theory.


Reading and writing Snowflake tables from Spark


The Horizon endpoint lives at https://<account_identifier>.snowflakecomputing.com/polaris/api/catalog - the Polaris path is not a coincidence. Auth is External OAuth, a key-pair JWT exchanged for an access token, a Programmatic Access Token, or Workload Identity Federation. The scope parameter carries the Snowflake role, so your existing RBAC follows the connection.


Python - Spark against Horizon Catalog with vended credentials
from pyspark.sql import SparkSession

ACCOUNT = "myorg-myaccount"
CATALOG = "analytics"              # the Snowflake database name
ROLE    = "session:role:spark_writer"
TOKEN   = "<access_token>"         # PAT, External OAuth or key-pair exchange

spark = (
    SparkSession.builder.appName("horizon-iceberg")
    .config("spark.sql.extensions",
            "org.apache.iceberg.spark.extensions.IcebergSparkSessionExtensions")
    .config(f"spark.sql.catalog.{CATALOG}",
            "org.apache.iceberg.spark.SparkCatalog")
    .config(f"spark.sql.catalog.{CATALOG}.type", "rest")
    .config(f"spark.sql.catalog.{CATALOG}.uri",
            f"https://{ACCOUNT}.snowflakecomputing.com/polaris/api/catalog")
    .config(f"spark.sql.catalog.{CATALOG}.warehouse", CATALOG)
    .config(f"spark.sql.catalog.{CATALOG}.token", TOKEN)
    .config(f"spark.sql.catalog.{CATALOG}.scope", ROLE)
    .config(f"spark.sql.catalog.{CATALOG}.header.X-Iceberg-Access-Delegation",
            "vended-credentials")
    .getOrCreate()
)

spark.sql(f"SELECT order_status, COUNT(*) "
          f"FROM {CATALOG}.public.orders_iceberg GROUP BY 1").show()

Read the limitations page first. Only Snowflake-managed Iceberg tables are reachable, not native tables and not externally managed ones. CTAS from an external engine, equality deletes, and creating Iceberg tags or branches are unsupported, and cloned or converted tables do not work with vended credentials. User-level network policies are unsupported here too, which catches security teams who standardised on them.


The cost line nobody has modelled: Iceberg REST API calls


Here is the trap. Snowflake documents Horizon Iceberg REST Catalog API requests at 0.5 credit per million calls, charged as Cloud Services, with billing "scheduled to begin in second half of 2026, subject to change". It is the only per-call catalog charge among the four, and because it is a new line rather than an established one, almost nobody has a baseline for their own call volume. Confirm the current rate and start date with your account team before you build a number on it.


Monthly IRC callsCreditsWhat generates this
1 million0.5A handful of Spark jobs, hourly
50 million25A few hundred tables polled every few minutes
500 million250Chatty clients doing per-partition metadata loads
2 billion1,000An agent or BI tool refreshing metadata per query

Those rows are arithmetic from the documented rate, not measured benchmarks, and the right-hand column is illustrative - your own call volume is the only number that matters. The rate is not scary; call amplification is. A client issuing loadTable on every task rather than caching, or an orchestrator health-checking every table every minute, puts two orders of magnitude between a well-behaved deployment and a badly configured one. Budget for cross-region egress too: Snowflake's docs say standard cross-region egress charges apply, so an engine in a different region from the account pays twice.


SQL - baseline your Cloud Services credits before per-call billing starts
SELECT
    DATE_TRUNC('day', start_time)              AS usage_day,
    service_type,
    ROUND(SUM(credits_used_compute), 2)        AS compute_credits,
    ROUND(SUM(credits_used_cloud_services), 2) AS cloud_services_credits,
    ROUND(
        100 * SUM(credits_used_cloud_services)
            / NULLIF(SUM(credits_used_compute), 0), 1
    ) AS cloud_svc_pct_of_compute
FROM snowflake.account_usage.metering_history
WHERE start_time >= DATEADD('day', -60, CURRENT_TIMESTAMP())
GROUP BY 1, 2
ORDER BY usage_day DESC, cloud_services_credits DESC;

One thing that softens the blow, and one caveat on it. Snowflake charges for Cloud Services only when daily Cloud Services consumption exceeds 10% of that day's virtual warehouse usage, calculated daily in UTC, with serverless compute excluded from the calculation. So an account already running warehouses hard has headroom. An account whose Snowflake compute is small relative to its external-engine traffic does not, and that is exactly the shape of a lakehouse where Spark and Trino do the work. Whether this specific line item falls under that adjustment is a question for your account team, not a blog.


Pointing Snowflake at an external catalog instead


The mirror-image design: keep Glue, Unity or Polaris as the source of truth and let Snowflake be one of the engines. Write support for externally managed Iceberg tables and catalog-linked databases has been GA since October 2025, so Snowflake runs INSERT, UPDATE, DELETE and MERGE against tables it does not own.


SQL - catalog integration to AWS Glue, then a catalog-linked database
CREATE OR REPLACE CATALOG INTEGRATION glue_rest_int
  CATALOG_SOURCE = ICEBERG_REST
  TABLE_FORMAT   = ICEBERG
  CATALOG_NAMESPACE = 'sales'
  REST_CONFIG = (
    CATALOG_URI      = 'https://glue.us-east-1.amazonaws.com/iceberg'
    CATALOG_API_TYPE = AWS_GLUE
    CATALOG_NAME     = '123456789012'          -- AWS account ID
  )
  REST_AUTHENTICATION = (
    TYPE                 = SIGV4
    SIGV4_IAM_ROLE       = 'arn:aws:iam::123456789012:role/snowflake-glue-role'
    SIGV4_SIGNING_REGION = 'us-east-1'
  )
  ENABLED = TRUE;

-- Auto-discovers namespaces and tables, and stays in sync
CREATE DATABASE glue_lakehouse
  LINKED_CATALOG = (
    CATALOG                  = 'glue_rest_int',
    ALLOWED_NAMESPACES       = ('sales', 'marketing'),
    ALLOWED_WRITE_OPERATIONS = ALL,
    SYNC_INTERVAL_SECONDS    = 60
  );

SELECT COUNT(*) FROM glue_lakehouse.sales.orders;

Swap REST_AUTHENTICATION for TYPE = OAUTH with OAUTH_CLIENT_ID, OAUTH_CLIENT_SECRET and OAUTH_TOKEN_URI to point at Polaris or Snowflake Open Catalog, or TYPE = BEARER for a generic REST catalog. Set ALLOWED_WRITE_OPERATIONS = NONE for a read-only consumer - the documented values are only NONE and ALL, so there is no partial-write middle ground, and anything that would commit back to the remote catalog is off the table. Snowflake polls the linked catalog on SYNC_INTERVAL_SECONDS, defaulting to 30 seconds, so discovery lag is a tuning knob rather than a surprise. Open Catalog is Snowflake's managed Apache Polaris service, with the same principal-role and catalog-role RBAC. It can federate to catalogs managed elsewhere, but Snowflake's docs are explicit that tables synced in from an external catalog are read-only inside Open Catalog: federation is discovery, not a write path.


Gotchas that decide real designs



The verdict, per platform gravity



The one answer that is always wrong to the question of which Iceberg catalog you should use is that you will decide later because the data is open anyway. The moment the first engine commits through a catalog, that catalog owns your table state, and swapping it means re-registering every table and re-testing every writer.


Pranay Vatsal, Founder & CEO

Pranay Vatsal is the Founder & CEO of CelestInfo with deep expertise in Snowflake, data architecture, and building production-grade data systems for global enterprises.

Related Articles

Frequently Asked Questions

Q: Do I still need an S3 bucket for Snowflake Iceberg tables?

Not since June 1, 2026. Setting EXTERNAL_VOLUME = SNOWFLAKE_MANAGED tells Snowflake to store and manage the Iceberg data and metadata files for you, with no external volume object, bucket policy or IAM trust relationship, and Fail-safe on permanent tables. The catch: Snowflake documents it as available only for accounts hosted on AWS or Azure, and not in government regions or in the People's Republic of China, so GCP accounts still use external volumes.

Q: Can Spark write to Snowflake Iceberg tables?

Yes. Write support from any external engine speaking the Iceberg REST protocol reached general availability on May 26, 2026, covering Snowflake-managed v2 and v3 tables through the Horizon Catalog endpoint. The engine's role needs SELECT, INSERT, UPDATE, DELETE and TRUNCATE on the table plus USAGE on the parent database and schema. CTAS from an external engine is not supported.

Q: Is Apache Polaris production ready?

It graduated from the Apache Incubator to a top-level Apache project on February 19, 2026, the ASF's signal that community and governance are mature, and releases have kept a steady cadence since - 1.5.0 in May 2026, 1.6.0 in July, 1.7.0 in August. Two things to know before you commit: the external authorizer integrations, Open Policy Agent and Apache Ranger, are both documented as beta, and Generic Tables, the non-Iceberg format path, do not support credential vending.

Q: What does the Snowflake Iceberg REST API cost?

Snowflake documents Horizon Iceberg REST Catalog API requests at 0.5 credit per million calls, charged as Cloud Services, with billing scheduled to begin in the second half of 2026 and explicitly subject to change. Cross-region data egress applies on top. Baseline your call volume now, because chatty clients amplify this by two orders of magnitude.

Q: Polaris vs Glue for Iceberg on AWS - which should I pick?

If your engines are EMR, Athena and Redshift and your permissions already live in Lake Formation, Glue is the lower-friction answer and adding Polaris buys complexity rather than neutrality. Choose Polaris when you need nested namespaces, non-Iceberg formats through Generic Tables, external authorizer hooks such as OPA or Ranger (both still beta), or a catalog not tied to one cloud.

Q: Can Unity Catalog serve Delta tables to external Iceberg engines?

Read-only. Over the Unity Catalog Iceberg REST endpoint, managed Iceberg tables support read and write, while Delta tables with Iceberg reads enabled and foreign Iceberg tables are read-only. Credential vending is separately unavailable for tables carrying row filters or column masks. Databricks' cross-engine ABAC, currently in beta, gives external engines a read path to those tables with the policies enforced server-side, but it is reads only and it needs recent client versions.