Back to blog

Apache Polaris: Deploy a Production Iceberg REST Catalog

Apache Polaris is the open-source reference implementation of the Iceberg REST Catalog specification. This guide covers what Polaris actually does, how it fits into a production lakehouse, and what it takes to deploy, secure, and operate it — from persistence and realm bootstrap through credential vending, multi-engine access, RBAC, federation, and the operational concerns the quickstart does not mention.

Jonathan Saring
Apache IcebergApache PolarisREST CatalogIceberg CatalogLakeOps

Jonathan Saring

27 min read
Apache Polaris — deploy and operate a production Iceberg REST Catalog

The Apache Polaris quickstart takes four minutes. Pull a container, hit the OAuth endpoint, create a catalog, point Spark at it, write a table. It works. It also stores everything in memory, signs tokens with keys generated at startup, and loses the entire catalog when the pod restarts.

The distance between that container and a catalog your data platform depends on is not conceptual — nobody gets confused about what a catalog does. The distance is operational: which persistence backend, how realms get bootstrapped, how multiple replicas agree on token signing, what credential vending actually requires from your cloud IAM, how five different engines each connect to the same tables, and which failures take the whole lakehouse offline versus which degrade quietly.

This guide covers that operational layer. Apache Polaris graduated to a Top-Level Project at the Apache Software Foundation in February 2026. It is the open-source reference implementation of the Iceberg REST Catalog specification, originally co-created by Dremio and Snowflake before being donated to the ASF. For a broader comparison of how Polaris fits alongside Glue, Nessie, Unity Catalog, Gravitino, and Lakekeeper, see the catalog comparison guide.

What Polaris actually does — and what it holds

Polaris is a catalog server. It implements the Iceberg REST Catalog API for table operations, plus its own management API for things the Iceberg specification does not cover: catalogs, principals, roles, grants, and storage configuration.

Its job in one sentence: for a given table, hold the pointer to the current metadata file, and swap that pointer atomically when a writer commits.

Everything else follows from that sentence. Because the pointer swap is the atomicity mechanism for the lakehouse, the store holding those pointers is the correctness boundary. Because engines get their storage access through the catalog, Polaris is also the authorization boundary. And because every query from every engine starts with a catalog call, it is on the critical path for all read and write traffic.

Three consequences shape every production decision:

  • The database matters more than the server. Polaris pods are stateless. Losing one costs you in-flight requests. Losing the database costs you the mapping between table names and metadata files — a mapping that is not easily reconstructable from object storage.
  • Availability is table-stakes. A catalog outage is not a degraded experience. Queries fail, writes fail, scheduled jobs fail — together. Single-replica deployments are a development pattern.
  • Security posture is centralized here. The storage credentials engines use come from this service. A misconfiguration's blast radius is every table in the catalog.

But one consequence the Polaris documentation does not address: the catalog stores pointers and controls access. It does not monitor whether the tables those pointers reference are healthy. It has no opinion on whether your tables need compaction, whether snapshots are drifting past retention, or what sort order would match the queries hitting each table. Access governance — who can read and write which tables — lives in the catalog. Operational governance — how tables are maintained, retained, compacted, and audited — does not. The catalog is the registry. It is not the operations layer.

A control plane such as LakeOps sits above the catalog — Polaris, Glue, Nessie, S3 Tables, or any REST-compatible catalog — and supplies the operational loop those components do not provide: observe every table's structural health, classify what each one needs, execute maintenance in dependency order, and verify the result. It connects through standard catalog and Iceberg metadata APIs with no data copy and no pipeline changes.

Lakehouse control plane — LakeOps above catalogs, object storage, and query engines
The control plane sits between catalogs (Glue, REST/Polaris, Nessie, S3 Tables) and query engines (Spark, Trino, Flink, Snowflake, Athena, DuckDB). It provides observability, autonomous maintenance, cost management, routing, governance, and AI guardrails — reading metadata and engine telemetry without copying data.

We will return to how this applies specifically to Polaris deployments after covering the deployment itself.

Persistence: choose the backend before anything else

This choice is close to irreversible in practice.

In-memory is the default in the Helm chart and the container quickstart. Everything lives in the process. A restart loses the entire catalog. It exists for demos and tests. The most common Polaris production incident comes from someone running the default chart values and discovering this property empirically.

Relational JDBC is the production path for most deployments. The metastore is a Quarkus-managed datasource that supports PostgreSQL for production and H2 for local development. This is where the majority of deployments should land.

MongoDB (NoSQL) exists as a beta option. Choose it when you have strong operational reasons and a team that runs MongoDB confidently.

The JDBC configuration surface is small:

properties
1polaris.persistence.type=relational-jdbc2quarkus.datasource.db-kind=postgresql3quarkus.datasource.jdbc.url=jdbc:postgresql://polaris-db.internal:5432/polaris4quarkus.datasource.username=polaris_app5quarkus.datasource.password=${POLARIS_DB_PASSWORD}

Every parameter is settable through environment variables using the Quarkus convention, which is what the Helm chart and the admin tool both rely on.

Database decisions that matter

Sizing. Catalog metadata is small in bytes but hot in access pattern. Rows are counted in thousands to low millions for most deployments. The working set fits in memory on a modest instance. Size for connection count and IOPS rather than storage — every table load from every engine is a query here.

Connection pooling. Each Polaris replica holds a connection pool. Three replicas at a default pool size against a small Postgres instance exhaust max_connections faster than people expect. Set the pool explicitly and do the arithmetic: replicas × pool size must stay under the database limit with headroom for admin sessions. Tune with the advancedConfig section in the Helm chart:

yaml
1advancedConfig:2  quarkus.datasource.jdbc.min-size: "5"3  quarkus.datasource.jdbc.max-size: "20"4  quarkus.datasource.jdbc.acquisition-timeout: "30S"5  quarkus.datasource.jdbc.idle-removal-interval: "5M"

Isolation. Give Polaris its own database instance, or at minimum its own cluster with dedicated resource limits. Sharing a Postgres with an application that runs occasional heavy analytics puts your entire lakehouse behind someone else's table scan.

Managed Postgres on AWS. The JDBC layer supports the Amazon RDS wrapper plugin, letting Polaris authenticate to Aurora PostgreSQL with IAM instead of a static password. This eliminates credential rotation for the database connection itself:

properties
1quarkus.datasource.jdbc.url=jdbc:postgresql://polaris-cluster.cluster-xyz.us-east-1.rds.amazonaws.com:5432/polaris2quarkus.datasource.jdbc.additional-jdbc-properties.wrapperPlugins=iam3quarkus.datasource.jdbc.additional-jdbc-properties.ssl=true

Realm bootstrap: the required first step everyone misses

Polaris will not serve traffic against a fresh database until you bootstrap it. This is a deliberate, once-per-realm operation that creates the schema and the root principal.

The admin tool is a separate container from the server and connects directly to the metastore — it does not talk to the Polaris API. A bootstrap that succeeds against the wrong database produces a server that starts and serves an empty catalog, which is a confusing failure to debug at 2 AM.

On Kubernetes, bootstrap runs as a one-shot pod that reads datasource values from the secret the chart created:

bash
1kubectl run polaris-bootstrap -n polaris \2  --image=apache/polaris-admin-tool:latest \3  --restart=Never --rm -it \4  --env="polaris.persistence.type=relational-jdbc" \5  --env="quarkus.datasource.username=$(kubectl get secret polaris-persistence -n polaris \6      -o jsonpath='{.data.username}' | base64 --decode)" \7  --env="quarkus.datasource.password=$(kubectl get secret polaris-persistence -n polaris \8      -o jsonpath='{.data.password}' | base64 --decode)" \9  --env="quarkus.datasource.jdbc.url=$(kubectl get secret polaris-persistence -n polaris \10      -o jsonpath='{.data.jdbcUrl}' | base64 --decode)" \11  -- bootstrap -r prod-realm -c prod-realm,root,$ROOT_SECRET

Three things about this command deserve more attention than they usually get:

The credential is a bootstrap credential, not a permanent identity. The -c realm,client-id,client-secret argument sets the root principal's initial credentials. That principal is the most privileged identity in the realm. Store the secret in a secrets manager, use it once to create the roles and service principals your platform actually uses, and never wire it into a pipeline.

Bootstrap is per-realm and runs once. Re-running it against an initialized realm fails. Script it as a Kubernetes Job rather than running it by hand. Recent versions of Polaris include an idempotent check, but older releases throw a raw exception on re-bootstrap.

Post-bootstrap setup runs through the management API. Create a catalog with its storage configuration, create catalog roles with the appropriate grants, create principals for each engine or team, create principal roles, and bind them. Commit this as a reproducible script (Terraform module, idempotent Python script, or Helm post-install hook). Having it reproducible is what makes disaster-recovery drills possible.

Realms: the tenancy boundary

A realm is a fully isolated Polaris instance sharing the same server process and the same database. Separate catalogs, separate principals, separate grants, separate root credentials. Nothing crosses between realms.

Realms answer a question teams usually try to answer with separate deployments: how do you keep production and staging apart without running two clusters? One Polaris deployment with prod and staging realms costs a fraction of two deployments and gives real isolation of the object model.

The limits are worth stating plainly. Realms share the database (so they share its availability), the server pods (so they share memory pressure and connection pool), and the deployment lifecycle (so an upgrade moves all of them at once). Realm isolation is an object-model boundary, not a blast-radius boundary. Use realms for environments and business units inside one operational domain. Use separate deployments when the requirement is regulatory isolation, independent upgrade cadence, or separate availability targets.

Deployment on Kubernetes

The official Helm chart is the supported path onto Kubernetes:

bash
1helm repo add polaris https://downloads.apache.org/polaris/helm-chart2helm repo update3kubectl create namespace polaris4helm install polaris polaris/polaris \5  --namespace polaris \6  --values production-values.yaml

The chart defaults target development. Four areas need explicit production values.

Replicas and topology

Run at least three replicas spread across failure domains. Three replicas on three nodes in one availability zone protects against a node failure — not against a zone failure. A catalog outage takes every engine with it.

yaml
1replicaCount: 32 3autoscaling:4  enabled: true5  minReplicas: 36  maxReplicas: 87  targetCPUUtilizationPercentage: 808 9topologySpreadConstraints:10  - maxSkew: 111    topologyKey: "topology.kubernetes.io/zone"12    whenUnsatisfiable: "DoNotSchedule"

Resources and priority

Set requests and limits equal for a service on the critical path. The project's production guidance uses 8 GiB of memory and 4 CPUs as a starting point per pod. The JVM heap sits inside that limit, so adjust from measurement rather than from assumption.

yaml
1resources:2  requests:3    memory: "8Gi"4    cpu: "4"5  limits:6    memory: "8Gi"7    cpu: "4"8 9priorityClassName: "polaris-high-priority"

Give the pods a PriorityClass above your batch workloads. A catalog evicted to make room for a Spark executor is a self-inflicted outage.

Token signing keys — the strangest production bug

By default the chart uses internal authentication with auto-generated signing keys. With multiple replicas, each pod generates its own key, and a token minted by pod A fails validation on pod B.

What you observe is intermittent 401 errors at roughly 1/N of your request rate, where N is the replica count. It looks like a load balancer problem and it is not.

All replicas must share the same signing key material. Provision RSA key pairs or symmetric keys as a Kubernetes secret, reference them in your values file, and rotate deliberately on a schedule. The Polaris docs support both RSA key-pair and symmetric key modes — choose RSA for environments that need to distribute the public key independently (e.g., external OIDC verification), symmetric for simpler deployments where all verification happens inside Polaris.

Credential vending: how Polaris eliminates long-lived storage keys

Credential vending is one of the strongest architectural arguments for a REST catalog over older catalog protocols. Instead of configuring every engine with permanent S3 access keys or GCS service account credentials, the catalog issues short-lived, prefix-scoped tokens at query time.

The mechanism works as follows:

  1. 1.The engine authenticates to Polaris via OAuth2 client credentials and receives an access token.
  2. 2.When loading a table, the engine includes X-Iceberg-Access-Delegation: vended-credentials in the request header.
  3. 3.Polaris checks the principal's RBAC grants for that table.
  4. 4.If authorized, Polaris calls the cloud provider's token service — AWS STS AssumeRole, Azure managed identity, or GCP service account impersonation — requesting a credential scoped down to the table's storage prefix and the operations the principal is allowed to perform.
  5. 5.The engine receives short-lived credentials in the LoadTableResult and uses them to access object storage directly.
  6. 6.Credentials expire in 15–60 minutes. The Iceberg client handles refresh transparently.

The security implications are significant:

  • No permanent storage keys in engine configurations. If a Spark job's credentials leak, the blast radius is one table for 15 minutes, not every table forever.
  • Permission alignment. A principal with SELECT-only grants on a table gets read-only storage credentials. No over-provisioning.
  • Audit granularity. Polaris supports AWS STS session tags so that storage access in CloudTrail correlates back to the Polaris principal, not just to the IAM role.
  • Multi-cloud from a single deployment. A single Polaris instance can vend credentials for tables in S3, ADLS, and GCS simultaneously.

One production gotcha: vended credentials are short-lived by design. A query that runs longer than the credential TTL fails partway through unless the Iceberg client refreshes the token. Most Iceberg clients handle this correctly, but some older clients do not — and the symptom is a long query failing on an access-denied error that appears unrelated to authentication. Test with a query that exceeds your credential TTL before going live.

To enable credential vending on a catalog, set the property during creation or update:

bash
1./polaris catalogs update my-catalog \2  --set-property "enable.credential.vending"="true"

The storage configuration — IAM role ARNs for AWS, application registrations for Azure, service accounts for GCP — is set at catalog creation time and stored in the Polaris metastore. Engines never see these long-lived identities. They see the short-lived, scoped tokens that Polaris mints on their behalf.

RBAC: centralized access control across engines

Polaris implements role-based access control at catalog, namespace, and table granularity. The model has two layers:

  • Catalog roles define what operations are allowed on which catalog objects. A data_reader catalog role might have TABLE_READ_DATA on all tables in the analytics namespace. A pipeline_writer role might have TABLE_WRITE_DATA and TABLE_CREATE on specific namespaces.
  • Principal roles bind catalog roles to principals. A Trino service principal gets the data_reader principal role; a Flink ingestion principal gets the pipeline_writer role.

The key advantage over storage-level IAM is uniformity. Every engine — Spark, Trino, Flink, Athena, DuckDB, Snowflake — goes through the same RBAC model. You do not need to manage separate S3 bucket policies for each engine's IAM role. The access control is expressed once in Polaris and enforced for every engine through the REST API.

For teams using external identity providers, Polaris supports OIDC with providers like Keycloak, Okta, and Entra ID. This lets human users authenticate through SSO and have their access governed by the same catalog role structure. Most production deployments use internal authentication for machine principals (engines, pipelines) and external OIDC for human operators and data consumers.

Multi-engine connectivity: how engines talk to Polaris

Polaris implements the Iceberg REST Catalog specification, which means every engine that supports the REST catalog protocol can connect. In practice, each engine has its own configuration surface and its own quirks. Here is what the connection looks like for the engines you are most likely running.

Spark connects through SparkCatalog with type=rest. It supports the full write path, including rewrite_data_files, expire_snapshots, and all maintenance procedures. Spark is the reference client for the Iceberg REST protocol — if something works anywhere, it works in Spark.

Trino configures the iceberg connector with iceberg.catalog.type=rest. Trino supports reads and writes, including schema evolution and table creation. It handles credential vending natively through REST. For Trino-specific S3 settings, the native filesystem must be enabled on the Trino side even when credentials are vended — Polaris returns the S3 endpoint, path-style flag, and region in the load-table response.

Flink uses the REST catalog for streaming ingestion with exactly-once semantics. Flink checkpoint commits become Iceberg commits through the catalog's atomic swap, making streaming writes safe. The main operational concern is Flink's high commit frequency — 5-minute checkpoints produce 288 commits per day per table, each adding a snapshot and manifest entries.

DuckDB connects through the iceberg extension with REST catalog support. DuckDB reads Iceberg tables efficiently for single-node analytics and notebook workflows but does not support writes or maintenance procedures. It benefits heavily from well-compacted tables — DuckDB has no distributed execution layer, so fragmented tables with tens of thousands of small files can overwhelm its query planner.

Athena can access REST catalogs through its Iceberg connector configuration. Athena is scan-priced, which means storage layout directly determines query cost. A table with 50,000 small files scattered across partitions costs significantly more to query on Athena than the same data compacted into a few hundred optimally sized files — even though the logical result is identical.

Snowflake reads external Iceberg tables through its Iceberg catalog integration, including REST catalogs. Snowflake's external table support provides read access through its own query engine while the catalog of record remains Polaris. The configuration registers the REST catalog URL and credential in Snowflake's external volume and catalog integration objects.

The shared pattern across all engines: configure the REST catalog URL, provide OAuth2 credentials, and let the catalog handle storage access through credential vending. Adding a new engine is a configuration change — a property file or a catalog registration call — not an integration project. This is the core value of the REST protocol: the engine-catalog integration matrix collapses from O(engines × catalogs) to O(engines + catalogs).

Federation: connecting existing catalogs through Polaris

Not every team can migrate all tables to Polaris on day one. Federation addresses this by letting a Polaris instance act as a routing layer for tables that live in other catalogs.

Polaris supports two federation paths:

Iceberg REST federation connects to another REST catalog (another Polaris instance, AWS Glue's REST endpoint, or a custom implementation). Enable the feature flag and register the external catalog:

properties
1polaris.features."ENABLE_CATALOG_FEDERATION"=true

The external catalog is registered through the management API with its connection URI and authentication configuration. Polaris forwards table operations to the remote catalog while applying its own RBAC. Supported authentication types include OAuth2, bearer tokens, and SigV4 (for Glue).

Hive Metastore federation connects to an existing HMS over Thrift. This is useful for incremental adoption — teams can expose Hive-managed Iceberg tables through the REST protocol without migrating them, giving modern engines REST access to legacy catalog infrastructure. For a detailed walkthrough of catalog migration patterns, see the catalog migration guide.

Federation has meaningful constraints. Federated catalogs are read-only for registration operations. RBAC for federated tables requires the sub-catalog RBAC feature flag. And federation exposes only Iceberg tables — generic table federation is not implemented.

The practical value is clear: a single Polaris endpoint can serve tables from Glue, HMS, and its own internal catalogs, giving engines one REST URL to configure. For teams with existing catalog infrastructure, federation makes Polaris an additive layer rather than a replacement.

LakeOps catalogs connected — multi-catalog connectivity across Glue, REST, DynamoDB, and S3 Tables
A control plane connects to multiple catalog types simultaneously — Glue, REST (Polaris, Nessie, Gravitino), DynamoDB, and S3 Tables — providing unified observability and maintenance across all of them. When Polaris federates external catalogs, the control plane sees every table regardless of which underlying catalog holds the pointer.

Production checklist: what to configure beyond the defaults

The Polaris documentation includes a production configuration checklist. Here is the operational translation, ordered by impact:

AreaDefaultProduction settingWhy it matters
PersistenceIn-memoryPostgreSQL (JDBC)Data survives restarts
Replicas13+ across AZsCatalog outage = lakehouse outage
Token signingAuto-generated per podShared secret across replicasEliminates intermittent 401s
Realm bootstrapNot doneScripted as K8s JobServer will not serve without it
OAuth2Internal auto-configExplicit keys + OIDCProduction authentication
Realm header validationDisabledrequire-header=truePrevents cross-realm requests
Local FILE storageEnabledDisabledNo local filesystem in production
Resource requests/limitsUnset8 GiB / 4 CPU equal req=limitPrevents eviction and OOM
Priority classNoneHigh priorityCatalog > batch workloads
Location overlap flagsPermissiveRestrictiveStorage isolation between tables

Two often-overlooked items deserve emphasis:

Disable local FILE storage. By default Polaris allows FILE as a storage type. In production, catalog storage should be S3, GCS, or ADLS. Set SUPPORTED_CATALOG_STORAGE_TYPES to exclude FILE explicitly.

Lock down location compatibility flags. ALLOW_EXTERNAL_METADATA_FILE_LOCATION, ALLOW_TABLE_LOCATION_OVERLAP, and wildcard allowed locations all relax default storage isolation. Keep them restrictive unless you have a specific interoperability or migration requirement and understand the security implications.

Operating Polaris in production

Upgrades and schema migrations

Polaris uses database schema versioning. When you upgrade the server to a new version, the first pod to start against the existing database applies any required schema migrations automatically. This means the first pod startup after an upgrade takes longer than normal and must succeed before other replicas can start. For zero-downtime upgrades, use a rolling deployment strategy and run the first upgrade during a maintenance window to establish the pattern.

Backups

A Polaris backup is a PostgreSQL backup. The catalog's operational data — table pointers, namespace hierarchy, RBAC grants, storage configurations — all lives in the database. Object storage (where the actual data and metadata files live) is not part of the Polaris backup.

Back up the database on a schedule that matches your recovery point objective. For most deployments, continuous WAL archiving to S3 with point-in-time recovery gives you sub-minute RPO. The realistic disaster is not a database crash — it is a bad script that drops a hundred tables at 14:32, and you want 14:31. Test the restore regularly.

Monitoring

Polaris exposes Quarkus-standard health endpoints (/q/health/live, /q/health/ready) for Kubernetes probes. For observability beyond liveness, instrument these signals:

  • Request latency on table load and commit operations (the critical path)
  • Database connection pool utilization (the first thing that saturates)
  • Token signing and validation errors (indicator of key misconfiguration)
  • 4xx and 5xx error rates by realm and catalog
  • Commit conflict rate (leading indicator that write concurrency has outgrown your table layout)
  • Credential vending rate and errors (tests the full path — a vending failure that returns 200 on the catalog API but scoped-wrong on the storage side is hard to catch without end-to-end probes)

Failure modes to understand

Database failure is the worst-case scenario. Every engine in the lakehouse loses the ability to resolve tables. Use a managed database service with automated failover and tune Polaris retry settings to ride through a 30-second failover without surfacing errors.

Single replica outage is a non-event with three or more replicas behind a load balancer. In-flight requests fail; subsequent requests route to surviving replicas.

Token signing key mismatch causes intermittent authentication failures that mimic load balancer bugs. The fix is shared key material. The diagnosis is checking whether the error rate correlates with 1/N where N is the replica count.

Unbootstrapped realm presents as authentication failures, not as a clear error message. If a new realm returns 401 for every request, check whether bootstrap has been run for that realm.

OAuth2 session stall. A known issue in Iceberg client 1.10.x: if a background token refresh fails (network blip, token endpoint down briefly), the AuthSession stops scheduling future refreshes permanently. Subsequent requests continue using the expired token and receive 401 errors until the process restarts. With multiple engine frontends behind a load balancer, this manifests as intermittent failures — some nodes serve the catalog fine while others reject every query. Monitor token refresh logs and consider recycling catalog sessions on refresh failure.

503 with reverse proxies. If you put Envoy, Nginx, or another proxy between engines and Polaris, be aware that a 503 response during a CreateTable commit can trigger the Iceberg client's cleanup path. If the commit actually succeeded on the Polaris side but the proxy returned 503, the client may delete valid manifest files — corrupting the table. This was addressed in Iceberg 1.10.1+, but verify your client version.

What operating Polaris costs

Polaris is free to run. Operating it is not. The infrastructure cost is modest — three pods, a managed Postgres instance, and a load balancer. On AWS, a production deployment typically runs $300–500/month for the catalog infrastructure (3× m5.xlarge or equivalent + db.r6g.large Aurora). The real cost is engineering time: initial setup takes 1–2 weeks for a team familiar with Kubernetes, and ongoing maintenance — upgrades, key rotation, monitoring, RBAC management, backup testing — takes roughly 10–20% of one engineer's time. For teams that prefer not to operate the catalog themselves, Snowflake Open Catalog provides a managed hosting option built on the same Polaris codebase.

What Polaris does not solve — the operational layer above the catalog

Polaris solves the metadata registry problem well. It holds table pointers, enforces access control, vends credentials, and provides the REST API that makes multi-engine Iceberg possible.

What it does not solve is the operational problem that begins the moment you have tables registered. Consider a typical deployment: 800 tables across four namespaces, written by Flink streaming jobs and Spark ETL, queried by Trino dashboards and DuckDB notebooks. After six months of operation:

  • Streaming tables have accumulated tens of thousands of small files per partition. Each Flink checkpoint commit adds files averaging 5–15 MB — far below the 256–512 MB target where engines perform well.
  • Silver-layer tables receiving MERGE INTO operations have accumulated thousands of delete markers that every read must reconcile. Query latency has crept 2–3× higher, but the tables report the same row count and appear unchanged to basic monitoring.
  • 400 snapshots per table have accumulated on streaming tables. Each snapshot adds metadata overhead. Storage holds terabytes of data files that were logically replaced weeks ago but never physically expired.
  • Orphan files from failed compaction attempts and aborted writes are accumulating across the lake — pure storage cost with no analytical value.
  • Nobody knows which tables are healthy and which are degraded, because the catalog does not classify health. The only signal is when a user files a ticket about a slow query.

Polaris has no opinion on any of these questions. It is a catalog, not a maintenance system. And these are exactly the questions that determine whether your Iceberg lakehouse is healthy or silently degrading.

LakeOps dashboard — lake-wide health, operations, query acceleration, and cost savings
The dashboard shows what the catalog does not: lake-wide health distribution, total operations with trends, query acceleration from optimization, cost savings, and storage/CPU reduction — computed continuously from catalog metadata and engine telemetry across every connected catalog.

LakeOps connects to your Polaris catalog (and Glue, Nessie, S3 Tables, or any REST catalog) and provides the operational layer that the catalog does not include. It reads every table's metadata continuously, classifies each table as Healthy, Warning, or Critical based on structural signals — small files, manifest fragmentation, snapshot drift, delete accumulation, partition skew — and surfaces degradation before users file tickets.

LakeOps table health list — every table classified with health status, sizes, and last modified timestamps
Every table in every connected catalog classified by health — Critical, Warning, or Healthy — with record counts, sizes, and modification times. This is the observability surface the catalog does not provide.
LakeOps insights — table health severity with actionable issues
Proactive insights ranked by severity — from critical small-file pressure to informational sort-order recommendations. Each insight links to the maintenance operation that resolves it. Issues resolve automatically as operations complete.

Maintenance runs against tables in REST catalogs specifically

The maintenance engine runs autonomous maintenance in the correct dependency order: snapshot expiration first (to dereference files), then orphan cleanup (to reclaim storage), then compaction (to merge and sort the remaining files), then manifest rewriting (to consolidate the metadata tree). This sequencing prevents wasted work — compacting files that are about to be expired, or rewriting manifests before compaction changes the layout.

Compaction runs on a dedicated Rust engine built on Apache DataFusion — no JVM startup, no Spark clusters to provision. For tables registered in a REST catalog like Polaris, the engine authenticates through the same REST/OAuth2 flow engines use, receives vended credentials, reads and writes data files in object storage, and commits compacted manifests back through the catalog's atomic swap. The commit is conflict-aware: it knows which partitions have active writers and excludes them, retries on OCC conflicts automatically, and never expires snapshots that active readers depend on.

The compaction engine is also query-aware: it watches which columns your queries actually filter and join on across every connected engine (Spark, Trino, Flink, Athena, DuckDB, Snowflake), then physically re-sorts data to match. The result is file-level min/max statistics that engines can actually use for pruning — the difference between an unsorted large file and a sorted one is typically 8–12× in query speed on selective filters.

LakeOps layout simulations — query-aware sort strategies driven by actual query patterns
Layout simulations compare sort strategies against actual query patterns — field access frequency by SELECT, FILTER, and JOIN across the real query mix. Each simulation shows the resulting sort columns, average file size, and how the layout compares to the current baseline. The best strategy is applied automatically during compaction.
LakeOps adaptive maintenance — per-table compaction, expiry, rewrite, and orphan cleanup
Per-table adaptive maintenance: compaction strategy (bin-pack or sort), snapshot expiration retention, orphan cleanup, and manifest rewriting — each driven by the table's structural signals and write velocity, not by a global cron schedule.

Policies across catalog boundaries

When you run Polaris for multi-engine access alongside Glue for existing AWS workloads, the maintenance problem spans catalogs. A snapshot retention policy that covers only the Polaris catalog leaves the Glue tables unmanaged. A compaction script written for one catalog does not automatically apply to the other.

LakeOps governance defines declarative policies — compaction thresholds, retention windows, orphan cleanup schedules, sort strategies — that enforce themselves across every connected catalog. A policy scoped to a namespace applies identically whether the underlying catalog is Polaris, Glue, or S3 Tables. New tables inherit policies automatically. Every execution is logged with full audit trail — what ran, when, what changed, files before and after.

LakeOps policies — compaction, orphan, expiry, rewrite schedules across catalogs
Declarative policies for compaction, orphan cleanup, snapshot expiry, and manifest optimization — scoped by catalog, namespace, or table. Define once, enforce across every connected catalog including Polaris.

Audit trail across every operation

This is the two-layer governance model in practice: Polaris enforces access governance — RBAC, credential vending, who reads and writes each table. LakeOps enforces operational governance — how tables are maintained, how long snapshots are retained, which sort order matches the query mix. The layers are complementary; each handles what the other does not.

Polaris provides RBAC audit logs for who accessed what. But it has no concept of maintenance operations — because it does not perform any. When you need to know which tables were compacted last week, which snapshots were expired, which orphan files were removed, and what the before/after metrics looked like, that information comes from the control plane's event trail.

LakeOps events audit trail — full operations log across catalogs
Every maintenance operation logged with context — catalog, table, operation type, duration, files before and after, bytes reclaimed, and status. The audit trail spans every connected catalog and is filterable by time range, catalog, and operation type.

For teams deploying Polaris, the LakeOps platform connects in roughly ten minutes: register your Polaris catalog and object storage, and LakeOps discovers every table, scores its health, and either starts autonomous optimization or waits for manual approval — your choice. No agents to deploy, no data movement, no pipeline changes.

Minutes to value — four-step onboarding flow from connect to observability
The onboarding flow: connect your catalog and collect telemetry, choose manual or autonomous mode, operations run and optimize (compaction, snapshots, orphan cleanup, manifests), and observability and governance activate — metrics, health scores, routing, logs, and policies.

For a comprehensive walkthrough of all nine capability components, see the Managed Iceberg in 2026 deep dive.

Where Polaris fits in the catalog landscape

Polaris is the strongest default for teams that need genuine engine and cloud neutrality, fine-grained access control, credential vending, and the ability to federate existing catalogs incrementally. Glue is the path of least resistance for pure-AWS shops. Nessie is the answer when you genuinely need branch isolation for data CI/CD. Unity Catalog fits Databricks-centered platforms. Gravitino addresses multi-catalog federation at scale. Lakekeeper offers a lightweight Rust-native alternative with minimal dependencies.

For most organizations, the realistic path involves more than one catalog. You may run Glue for existing AWS workloads and Polaris for multi-engine access. You may add Nessie for development environments. The REST protocol makes this manageable — engines configure a URL, and the catalog behind it can change without touching application code.

Conclusion

Apache Polaris is the open-source reference implementation of the Iceberg REST Catalog — the specification that is becoming the standard interface between compute engines and table metadata. Deploying it in production requires deliberate choices about persistence, realm management, replica topology, token signing, credential vending, and RBAC that the quickstart does not cover.

The catalog is the foundation. It solves metadata resolution, access control, and credential management. What it does not solve is the operational layer above the catalog: which tables are healthy, which need maintenance, which snapshots should be expired, what sort order would match the cross-engine query patterns, and what each table costs to store and query. That is the gap a lakehouse control plane like LakeOps fills — connecting to your Polaris catalog (or any catalog) and providing the observability, autonomous maintenance, and governance that turns an open data lake into a managed lakehouse.

Tags

Apache IcebergApache IcebergApache PolarisREST CatalogIceberg CatalogLakeOps

Related articles

Found this useful? Share it with your team.