Skip to main content

Overview

Apache Polaris is an Apache Iceberg REST catalog — the service that tells a query engine which tables exist, where their metadata lives in object storage, and who may read them. It is the piece that turns a bucket of Parquet files into a lakehouse. This template completes a stack the catalog already has the other two thirds of: seaweedfs (or any S3-compatible bucket) stores the data, Polaris is the catalog, and trino runs the queries — all wired over internal GVC DNS, with no data leaving your organization. That exact path was verified end to end: Trino created schemas and tables through Polaris, inserted and read rows back, and the resulting Parquet data files and Iceberg metadata were confirmed in the SeaweedFS bucket from SeaweedFS’s own side. See Building a Lakehouse. Polaris itself is stateless. Every catalog, namespace, table pointer, principal and grant lives in a PostgreSQL metastore that the template deploys for you, so a replicas knob scales the catalog horizontally with no coordination to configure.

Architecture

  • Polaris server — A standard workload serving the Iceberg REST API and the Polaris management API on port 8181, and Quarkus health and metrics on port 8182. Stateless, with a replicas knob.
  • Bootstrap workload — Runs the official polaris-admin-tool image once to create the realm schema and the root principal, then idles. Always one replica, and idempotent — a restart or upgrade re-runs it harmlessly.
  • Metastore — A single-instance postgres by default, or postgres-highly-available with one flag.
  • Realm — Polaris isolates tenants by realm. This template ships exactly one and does not require the Polaris-Realm header, which is what makes Trino’s Iceberg REST connector work against it.

What Gets Created

  • Standard Polaris Workloadreplicas interchangeable replicas serving 8181 and 8182.
  • Standard Bootstrap Workload — One replica, no ports, unreachable from anywhere. It initializes the realm and then sleeps.
  • Bootstrap Script Secret — An opaque secret holding the shell script the bootstrap workload runs, mounted as a file.
  • Two Identities & Two Policies — Split for least privilege: only the bootstrap principal can reveal your root credentials, and only the server principal can reveal the token signing key and the object-storage credentials.
  • PostgreSQL Metastore — The postgres or postgres-highly-available subchart, with its own workload, volume set and config secret.
  • No Volume Set for Polaris — The server writes nothing to local disk that must survive a restart.
This template does not create a GVC. You must deploy it into an existing GVC.

Prerequisites

Two Control Plane secrets must exist before you install. They are referenced by name, never passed through values, and the deployment waits indefinitely on a secret that does not exist — an install that looks stuck is almost always a missing one of these.
1

Create the root credentials secret

A dictionary secret with CLIENT_ID and CLIENT_SECRET. These become the realm’s root principal at bootstrap, and are what Trino, Spark and any other Iceberg REST client authenticate with. Neither value may contain a comma.
2

Create the token signing key secret

An opaque secret whose payload is a random string of 32 or more characters. Every replica signs and validates access tokens with it, so tokens survive restarts and are accepted across replicas.
Both secrets are yours, not the release’s — helm uninstall leaves them in place. Optional:
  • Object-storage credentials for the bucket holding your Iceberg data — a dictionary secret with AWS_ACCESS_KEY_ID and AWS_SECRET_ACCESS_KEY. See Object Storage. Polaris runs without it; catalogs can be added later.
  • A cloud account and bucket only if you turn on the metastore’s backup pass-through. See Backing Up.

Installation

Install the template using your preferred method:

UI

Browse, install, and manage templates visually

CLI

Manage templates from your terminal

Terraform

Declare templates in your Terraform configurations

Pulumi

Declare templates in your Pulumi programs
A default install takes about 90 seconds, and part of it looks like a failure. While PostgreSQL is still starting, the Polaris server restarts a couple of times logging Failed to start quarkus, and the bootstrap workload logs attempt failed (metastore not ready yet?) - retrying in 10s. Both self-heal: the measured warm-up was 2 restarts within 32 seconds, after which the server started cleanly. Do not interrupt it. On the postgresHA metastore the same sequence takes about 6 minutes (measured 348 seconds), because Patroni and etcd have to converge first.

Configuration

The default values.yaml for this template:

Server and Replicas

  • image — The official upstream image. The chart is shipped and tested on Polaris 1.7.0.
  • replicas — Number of Polaris replicas (default 1, minimum 1). Replicas are interchangeable: they share the metastore and the token signing key, so a token minted by one is accepted by every other and a catalog created through one is immediately visible on the rest. Raise it for capacity and to keep serving through a rolling restart — see Availability.
  • resources.minCpu / maxCpu / minMemory / maxMemory — CPU and memory per replica. minMemory may not exceed maxMemory, and maxCpu:minCpu may not exceed 4:1 (a Control Plane limit); the chart refuses to render otherwise, naming the value to fix.

JVM and Memory

jvm.maxRAMPercentage (default 70, allowed range 40–80) sets the JVM heap as a percentage of resources.maxMemory, so maxMemory is normally the only number you change. Capacity AI is disabled on both workloads: the JVM sizes its heap from the container limit at startup, so shrinking the container afterwards would be an out-of-memory kill with no diagnostic.

Realm

realm (default POLARIS) is the tenant namespace the bootstrap workload creates. This template ships exactly one realm and does not require the Polaris-Realm header — a header-less request resolves to this realm, which is what Trino’s Iceberg REST connector needs, since it cannot send an arbitrary header. Requests naming a different realm are rejected with 404.
realm is permanent after the first install. Renaming it bootstraps a new, empty realm and hides the existing catalogs — they are not deleted, and changing the name back makes them visible again, but nothing is migrated between realms.

Credentials

  • rootCredentials.secretName — Name of the prerequisite dictionary secret holding CLIENT_ID and CLIENT_SECRET. Required.
  • tokenSigningKey.secretName — Name of the prerequisite opaque secret whose payload is the shared HMAC signing key. Required.
The signing key is mounted unconditionally, not only when replicas is above 1: Polaris’s own default generates a key per JVM process, so even a single replica would hand out tokens that its own restart rejects with 401.
Root credentials are write-once. They are applied when the realm is first bootstrapped; changing the secret afterwards has no effect on the existing realm. Rotate by creating a new principal through the management API instead. Rotating tokenSigningKey does take effect and invalidates every outstanding token — clients must request a new one.

Object Storage

  • storage.credentialsSecretName — Name of a dictionary secret with AWS_ACCESS_KEY_ID and AWS_SECRET_ACCESS_KEY, used for Polaris’s own Iceberg metadata reads and writes. Empty by default, which is a valid install: the server runs and answers, but catalogs backed by object storage cannot read or write.
  • storage.region — Value of AWS_REGION. S3-compatible servers ignore it, but the AWS SDK requires one.
With seaweedfs or minio in the same GVC, point this at the same dictionary secret that deployment already uses (s3.credentialsSecretName for SeaweedFS) — there is nothing else to create. For AWS S3, create an access key scoped to the bucket:
This version does not vend STS credentials. Polaris does not hand short-lived, scoped credentials to query engines, so each engine needs its own object-storage credentials in addition to these. Keep iceberg.rest-catalog.vended-credentials-enabled=false in Trino, and set stsUnavailable: true when you create a catalog on an S3-compatible server.

Bootstrap

bootstrap.image is the official admin tool image; keep its tag in lockstep with image. bootstrap.resources sizes the one-shot container. The bootstrap workload runs the admin tool against the metastore, creates the realm schema and the root principal, and then idles — it exits nothing and serves nothing, so it stays ready after its work is done. The operation is idempotent: on any restart or upgrade it logs Realm 'POLARIS' is already bootstrapped; skipping and idles again. Nothing calls it, and its internal firewall is set to none.

Access

  • publicAccess.enabledfalse by default. When true, the Iceberg REST API and the Polaris management API are served over HTTPS on the automatically assigned *.cpln.app canonical endpoint.
  • internalAccess.type — Which workloads inside Control Plane may reach Polaris:
Port 8182 is never published on the public endpoint. The canonical endpoint routes to the workload’s first declared container port, which is 8181. Polaris serves the unauthenticated Quarkus health, metrics and info endpoints only on 8182, so with public access on, /q/metrics, /q/health and /q/info all return 404 from the internet while :8182/q/metrics answers normally inside the GVC. Firewall changes take up to a couple of minutes to propagate.

Metastore

Exactly one of postgres (default) and postgresHA must be enabled — the chart enforces this at render and fails with a message naming the fix. Switch by setting postgres.enabled: false and postgresHA.enabled: true. Polaris is wired to whichever is active automatically — the single instance directly, or the HAProxy leader endpoint in HA mode. Change the database password before installing in either mode (postgres.config.password or postgresHA.postgres.password); the default is an obvious placeholder. Each Polaris replica opens up to 20 JDBC connections, plus the bootstrap workload — about 21 of PostgreSQL’s default 100 at replicas: 1, and about 61 at replicas: 3.

Building a Lakehouse

Storage, catalog, and engine are three separate templates. This is the whole path, verified end to end.
1

Deploy the storage layer

Install seaweedfs with a bucket for your warehouse (s3.buckets: [lakehouse]), or use minio or AWS S3. Note the dictionary secret holding its AWS_ACCESS_KEY_ID / AWS_SECRET_ACCESS_KEY.
2

Deploy Polaris against that bucket

Set storage.credentialsSecretName to that same secret, and create the two prerequisite secrets from Prerequisites first.
3

Get an access token

Every API call needs a bearer token, obtained with the root credentials:
4

Create a catalog

Catalogs are a day-2 API call, not an install-time value:
pathStyleAccess: true and stsUnavailable: true are what an S3-compatible server needs. Drop endpoint, endpointInternal and pathStyleAccess for AWS S3 itself.
5

Point Trino at it

Add this catalog to the trino template’s values. Trino authenticates to Polaris with the root credentials and talks to the bucket with its own S3 keys — credential vending is off, so both sides hold static credentials:
6

Query it

CREATE SCHEMA, CREATE TABLE, INSERT and SELECT now work against tables stored in the bucket:
Any other Iceberg REST client — Spark, PyIceberg, Flink — connects the same way: the REST URI, OAuth2 client credentials, and the warehouse name.

Connecting

Same-GVC clients use plain http:// over the mesh’s mTLS; external clients use https:// with TLS terminated at the platform edge. A request with no bearer token, or a wrong client secret, is rejected with 401.

Availability

Polaris is stateless and every replica shares both the metastore and the token signing key, so replicas is a real availability knob. Measured at replicas: 2 with an authenticated request every 0.5 s from another workload, through the service DNS name: Catalog state survived both events. At the default replicas: 1 a rolling upgrade replaces the only replica, so requests fail for the duration of the restart; raise replicas to keep serving through one. The metastore is the other half of the picture: with the default single-instance postgres, a database restart takes Polaris down with it for as long as PostgreSQL takes to come back. postgresHA removes that with Patroni failover, at the cost of a larger footprint and a slower first install.

Backing Up

Metastore backups are off by default and need no cloud account. Turn them on with postgres.backup.enabled: true (or postgresHA.backup.enabled: true) and configure a provider — the backup runs inside the PostgreSQL subchart, so this is that template’s mechanism, passed straight through.
Create the bucket, a Control Plane cloud account, and an IAM policy scoped to the bucket:
Then set provider: aws and the aws.bucket, aws.region, aws.cloudAccountName and aws.policyName values.
In HA mode, postgresHA.backup.mode selects logical (scheduled pg_dump) or wal-g (continuous WAL archiving).

Important Notes

  • Create both prerequisite secrets before installing. Without them the workloads wait on a secret that does not exist, and the install looks broken with no useful error.
  • Expect a warm-up and do not interrupt it. A default install is ready in about 90 seconds, including a 2-restart, ~32-second crash loop while PostgreSQL boots. The postgresHA metastore takes about 6 minutes before Polaris answers. Both are normal.
  • Root credentials are write-once; they are applied only at first bootstrap. Rotate by creating a new principal through the management API.
  • Rotating the token signing key invalidates every outstanding token — clients must request a new one.
  • realm is permanent. Renaming it bootstraps a new, empty realm and hides the existing catalogs.
  • Change the metastore password before installing (postgres.config.password or postgresHA.postgres.password) — the default is a placeholder.
  • No credential vending in this version. Polaris and each query engine hold their own static object-storage credentials; keep iceberg.rest-catalog.vended-credentials-enabled=false in Trino.
  • Health and metrics are in-GVC only. They are served on port 8182, which the public canonical endpoint does not route to.
  • Polaris does not migrate its own database schema. Treat a future Polaris version bump as an explicit schema step, not something startup handles.
  • Metastore volumes survive restarts, redeploys and upgrades; uninstalling deletes them — and every catalog definition with them. The Iceberg data in your bucket is untouched, but the catalog that indexed it is gone. Use postgresHA and/or backups for anything you care about.
  • Uninstall does not delete your prerequisite secrets — they are your resources, created outside the release.

External References

Apache Polaris Documentation

Official documentation for the shipped release

Configuration Reference

Every server setting and its environment-variable name

Relational JDBC Metastore

The PostgreSQL persistence backend this template uses

Admin Tool

The bootstrap tool the init workload runs

Creating a Catalog on S3

Storage configuration for S3 and S3-compatible servers

Access Control

Principals, roles, and grants beyond the root principal

Trino Iceberg REST Catalog

The connector properties used to point Trino at Polaris

Polaris Template

View the source files, default values, and chart definition