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. Together with SeaweedFS (or any S3-compatible bucket) for storage and Trino for queries, it completes an Iceberg stack wired entirely over internal GVC DNS. The Polaris server is stateless. Every catalog, namespace, table pointer, principal and grant lives in a PostgreSQL metastore that the template deploys for you, single-instance by default or highly available with one flag, so the replicas knob scales the catalog horizontally with nothing else to coordinate.
This template deploys into an existing GVC that you already have. It does not create one.

What Gets Created

In HA mode with postgresHA.backup.mode: wal-g, no backup cron workload is created; a wal-g-backup sidecar container runs inside RELEASE_NAME-postgres-ha instead. The Polaris server itself has no volume set.

Prerequisites

Two secrets must exist before you install. They are referenced by name and never pass through Helm values. Secrets are org-level, so no GVC flag is involved.
1

Create the root credentials secret

A dictionary secret holding CLIENT_ID and CLIENT_SECRET. These become the realm’s root principal at bootstrap, and they are what Trino, Spark and any other Iceberg REST client authenticate with. Neither value may contain a comma.
Set rootCredentials.secretName to this name.
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 by every replica.
Set tokenSigningKey.secretName to this name.
Create both secrets before installing, or the deployment wedges silently. A name that points at a secret which does not exist installs “successfully” and then never starts: the container never runs, so cpln logs returns zero lines. The one place the reason appears is status.versions[].message — check RELEASE_NAME-polaris for the signing key and RELEASE_NAME-polaris-bootstrap for the root credentials:
Use get-deployments; plain cpln workload get has no versions key. Creating the missing secret repairs the deployment on its own after several minutes, or force a redeployment to skip the wait:
The metastore password is not a prerequisite: this chart creates the metastore credentials secret from postgres.credentials.*. Change the password before installing. Optional, and not needed for the install itself:
  • Object-storage credentials for the bucket that holds your Iceberg tables. Polaris runs without them and catalogs can be added later — see Building a Lakehouse.
  • A bucket and Cloud Account for metastore backups — see Backing Up.

Installation

Install with your two secret names and a strong metastore password:

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
The first start takes a few minutes. While PostgreSQL is still coming up, the server may restart a few times and the bootstrap workload logs attempt failed (metastore not ready yet?) - retrying in 10s; both recover on their own once the metastore answers, so do not interrupt the install. The HA metastore takes noticeably longer than the single instance.

Configuration

Server

  • replicas — A fixed number of interchangeable Polaris replicas (minimum 1; there is no request-based autoscaling). See Scaling and Availability.
  • resources — Per replica. minMemory may not exceed maxMemory, and maxCpu may not exceed four times minCpu; the chart refuses to render otherwise and names the value to fix.

JVM

The JVM heap is this percentage of resources.maxMemory (allowed range 40–80), so maxMemory is normally the only number you change.

Realm

Polaris isolates tenants by realm. This template ships exactly one and does not require the Polaris-Realm header: a request without it resolves to this realm, which is what Trino’s Iceberg REST connector needs. A request naming a different realm is rejected with 404. The name may contain only letters, digits, hyphens and underscores.
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 moves between realms.

Credentials

  • rootCredentials.secretName — The prerequisite dictionary secret holding CLIENT_ID and CLIENT_SECRET. Required.
  • tokenSigningKey.secretName — The prerequisite opaque secret whose payload is the shared token signing key. Required.
Root credentials are write-once. They are applied when the realm is first bootstrapped, and changing the secret afterwards has no effect on the existing realm. To rotate, create a new principal through the Polaris management API. Rotating the signing key does take effect once the server is redeployed, and it invalidates every outstanding token, so clients must request a new one.

Object Storage

  • storage.credentialsSecretName — Name of a dictionary secret holding AWS_ACCESS_KEY_ID and AWS_SECRET_ACCESS_KEY, used for Polaris’s own reads and writes of Iceberg metadata. Empty by default, which is a valid install: the server runs and answers, but catalogs on object storage cannot read or write. Per-provider setup is in Building a Lakehouse.
  • storage.region — The bucket’s region. S3-compatible servers ignore it, but the AWS SDK requires a value.
This template 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 as well. 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

Keep bootstrap.image on the same tag as image. The bootstrap workload creates the realm schema and the root principal, then idles. The operation is idempotent: on a restart or upgrade it logs that the realm is already bootstrapped and idles again.

Access

With publicAccess.enabled: true, only port 8181 is served on the public endpoint; the health and metrics endpoints on 8182 stay reachable from inside the GVC only. A change to either access setting takes a few minutes to take effect, so re-test rather than trusting the first response.

Single-Instance Metastore

The default metastore. Exactly one of postgres and postgresHA must be enabled; the chart refuses to render otherwise.
  • postgres.credentials.* — The metastore’s username, password and database name. Change the password before installing. The chart writes these three values into the credentials secret in both metastore modes, even though they sit under the postgres block, and Polaris connects to the database named here.
  • postgres.config.credentialsSecretName — Name of the dictionary secret this chart creates in single-instance mode. Secret names are organization-wide, so give each Polaris release its own: a second release left on the default name is refused at install and creates nothing, and the first release is unaffected.
  • postgres.backup.* — Optional scheduled backups. See Backing Up.
The credentials are applied when the metastore volume is first initialized. Changing postgres.credentials.password on an existing release updates the secret but not the password PostgreSQL enforces, so Polaris can no longer authenticate. To rotate, change the password inside PostgreSQL first, then upgrade the release with the matching value and force a redeployment of RELEASE_NAME-polaris and RELEASE_NAME-polaris-bootstrap so they pick up the new value.

Highly Available Metastore

Set postgresHA.enabled: true and postgres.enabled: false for near-zero-downtime upgrades and automatic database failover: 3 Patroni PostgreSQL replicas, 3 etcd replicas and an HAProxy leader endpoint that Polaris connects through.
  • postgresHA.config.credentialsSecretName — Name of the dictionary secret this chart creates in HA mode, built from postgres.credentials.* above. Give each release its own name, as in single-instance mode.
  • postgresHA.replicas / postgresHA.volumeset.capacity — PostgreSQL replica count and volume size per replica. The remaining cluster settings take the defaults of the PostgreSQL Highly Available template; its HAProxy endpoint must stay enabled, because it is Polaris’s database address.
  • postgresHA.backup.* — Optional backups as scheduled logical dumps or continuous WAL-G archiving. See Backing Up.
Switching an existing release between modes is a new, empty metastore, not a migration.

Connecting

To get a token, request one with the root credentials. From your own machine, open a tunnel to the server first:
Then, in a second terminal:
The response carries the token in access_token; send it as Authorization: Bearer TOKEN. A missing token or a wrong client secret is rejected with 401. The same request works against the public base URL when publicAccess.enabled is true. Reaching Polaris through cpln port-forward has not been exercised against this template; if the tunnel does not answer, run the same request from a workload inside the GVC against the internal base URL.

Building a Lakehouse

Storage, catalog and query engine are three separate templates. This is the path from an empty bucket to queryable Iceberg tables.
1

Prepare a bucket and its credentials

Polaris reads and writes table metadata with static credentials, held in a dictionary secret with the keys AWS_ACCESS_KEY_ID and AWS_SECRET_ACCESS_KEY. Only S3 and S3-compatible storage are supported by this template.
Install SeaweedFS in the same GVC with a bucket for your warehouse (for example s3.buckets: [lakehouse]). Its s3.credentialsSecretName secret already holds AWS_ACCESS_KEY_ID and AWS_SECRET_ACCESS_KEY, so set Polaris’s storage.credentialsSecretName to that same secret. There is nothing else to create. Its S3 endpoint is http://SEAWEEDFS_WORKLOAD_NAME.GVC_NAME.cpln.local:8333.
Apply the setting to an existing release with cpln helm upgrade, using the same chart version and your other values.
2

Get an access token

Request a token as shown in Connecting and export it as TOKEN.
3

Create a catalog

Catalogs are created through the management API after install, not through values. Replace BASE_URL with your internal or public base URL, YOUR_BUCKET with your bucket, and the endpoint with your storage’s S3 endpoint:
pathStyleAccess: true and stsUnavailable: true are what an S3-compatible server needs. For AWS S3 itself, drop endpoint, endpointInternal and pathStyleAccess, and set region to the bucket’s region.
4

Point Trino at it

Add this entry to the Trino template’s catalogs value. Trino authenticates to Polaris with the root credentials and reaches the bucket with its own S3 keys, because credential vending is off:
POLARIS_WORKLOAD_NAME is your Polaris release’s RELEASE_NAME-polaris. iceberg.rest-catalog.warehouse is the catalog name you created.
5

Query it

CREATE SCHEMA, CREATE TABLE, INSERT and SELECT now work against tables stored in the bucket:

Other Iceberg Clients

Spark, PyIceberg, Flink and any other Iceberg REST client connect with the same four settings: the REST URI (BASE_URL/api/catalog), the OAuth2 client credential CLIENT_ID:CLIENT_SECRET, the scope PRINCIPAL_ROLE:ALL, and the catalog name as the warehouse. Each also needs its own object-storage credentials. Only the Trino path has been verified with this template. The Spark template’s image does not include the Iceberg Spark runtime, so follow the upstream Iceberg Spark configuration guide to add it.

Operations

Backing Up

Metastore backups are optional and disabled by default. They protect every catalog, namespace, table pointer, principal and grant; the Iceberg data files in your bucket are not part of them. Enable them with postgres.backup.enabled or postgresHA.backup.enabled, matching your metastore mode, and complete the setup for your provider before enabling. The keys below are written as backup.*; set them inside the enabled metastore block. In HA mode, postgresHA.backup.mode selects logical (a scheduled dump from the RELEASE_NAME-postgres-ha-backup cron workload) or wal-g (continuous WAL archiving from a wal-g-backup sidecar inside RELEASE_NAME-postgres-ha, with a base backup every walg.intervalSeconds). Single-instance mode takes scheduled logical dumps only.
1

Create a bucket

Create an S3 bucket. Set backup.aws.bucket and backup.aws.region to match.
2

Set up a Cloud Account

If you do not have one, create a Cloud Account for your AWS account. Set backup.aws.cloudAccountName to its name.
3

Create a bucket-scoped IAM policy

Create an AWS IAM policy with the JSON below (replace YOUR_BUCKET), then set backup.aws.policyName to the policy’s name:

Restoring a Backup

This restore path has not been exercised against a Polaris install. It follows the restore procedure of the bundled database templates. Rehearse it on a throwaway release before you depend on it.
The metastore backups are produced by the bundled database templates, so restore them with those templates’ procedures: PostgreSQL for single-instance mode, and PostgreSQL Highly Available for HA mode, using the host from Connecting and the credentials from your metastore credentials secret. Two Polaris-specific constraints apply:
  1. Keep realm identical to the value the backup was taken under, or the restored catalogs stay invisible.
  2. Restore into a metastore that does not already hold a bootstrapped realm. A new release bootstraps its realm on first start, so restoring over it collides with the objects the bootstrap created. After a restore, force a redeployment of RELEASE_NAME-polaris so every replica reconnects.

Upgrading from 1.0.x

Apply every subsection from your version onward, in order, before running cpln helm upgrade. A removed key is rejected at render, so an upgrade that still carries one fails before anything is applied and the running release is left untouched. Version 1.1.0 moved the single-instance metastore to the postgres 3.4.1 template, which no longer takes credentials or MinIO keys as values. This template creates the credentials secret itself, so there is no new prerequisite, only renames on the single-instance path: Carrying an old key forward fails with the bundled database template’s message, which begins config.username was REMOVED in postgres 3.4.0 and tells you to create a secret. Ignore that advice for the three credentials keys: this template creates that secret from postgres.credentials.*. Use the username and password your metastore was initialized with, not new ones.
Template version 1.0.0 did not compact the etcd cluster inside the HA metastore, so its backend grew with time alone and eventually went read-only, taking PostgreSQL failover with it. Only HA-mode installs are affected. Upgrading to 1.0.1 or later turns compaction on, which stops further growth but cannot shrink a backend that has already grown; a cluster that has already raised a NOSPACE alarm needs operator recovery rather than an upgrade. See etcd History Compaction.

Upgrading from 1.1.0

Version 1.2.0 moved the HA metastore to postgres-highly-available 2.5.0, which removed the postgresHA.postgres block. The HA cluster now reads the same chart-created credentials secret as the single-instance path:
Use the credentials your cluster already has. The HA cluster was initialized with the postgresHA.postgres.* values you installed with, and PostgreSQL still enforces them. Copy that username, password and database name into postgres.credentials.* before upgrading. New values there update the secret but not the database, and Polaris can no longer authenticate.
An upgrade that still carries the postgresHA.postgres block fails with the HA template’s message, which begins postgres-highly-available: the `postgres` block was REMOVED in 2.5.0 and tells you to create a dictionary secret. Ignore that part: delete the block and set postgres.credentials.* instead.

Upgrading from 1.2.0

HA MinIO backups only: version 1.2.1 replaced postgresHA.backup.minio.accessKey and postgresHA.backup.minio.secretKey with postgresHA.backup.minio.credentialsSecretName, a dictionary secret you create holding accessKey and secretKey — see Backing Up. An upgrade that still carries the old keys fails with backup.minio.accessKey and backup.minio.secretKey were REMOVED in 2.5.0. Create the secret before upgrading, because in wal-g mode the database workload itself references it.

Scaling and Availability

  • The server scales horizontally. Replicas share only the metastore and the token signing key, so a token minted by one replica is accepted by every other and a catalog created through one is visible on all of them. At the default replicas: 1, an upgrade replaces the only replica and requests fail while it restarts; with replicas: 2 or more, a rolling upgrade keeps serving.
  • Mind the metastore’s connection limit. Each Polaris replica opens up to 20 database connections, plus one for the bootstrap workload, against PostgreSQL’s default limit of 100.
  • The bootstrap workload always runs one replica and does nothing after its first run. Do not scale it.
  • The metastore is the other half of availability. With the default single-instance postgres, a database restart takes Polaris down until PostgreSQL is back. postgresHA adds automatic failover behind a stable endpoint, at the cost of a larger footprint.
  • The first upgrade after an install can restart the bundled database even when no database value changed. Expect a short outage on a single-instance metastore.
  • Uninstall deletes the metastore volume sets and the chart-created credentials secret, taking every catalog definition with them. The Iceberg files in your bucket survive, but nothing indexes them any more. Your two prerequisite secrets are not deleted.

Troubleshooting

Symptom: RELEASE_NAME-polaris or RELEASE_NAME-polaris-bootstrap never becomes ready and cpln logs returns zero lines.Cause: A prerequisite secret named by tokenSigningKey.secretName, rootCredentials.secretName or storage.credentialsSecretName does not exist.Fix: Read status.versions[].message with cpln workload get-deployments RELEASE_NAME-polaris --gvc GVC_NAME -o yaml, create the secret it names as shown in Prerequisites, then wait several minutes or force a redeployment.
Symptom: RELEASE_NAME-polaris-bootstrap restarts repeatedly, logging FATAL: neither CLIENT_ID nor CLIENT_SECRET may contain a comma.Cause: The admin tool takes the realm, client ID and client secret as one comma-separated argument.Fix: Recreate the root credentials secret without commas, for example with openssl rand -hex 24 as in Prerequisites, then force a redeployment of RELEASE_NAME-polaris-bootstrap.
Symptom: Render fails with polaris: enable exactly one metastore.Cause: Both postgres.enabled and postgresHA.enabled are true, or neither is.Fix: Set exactly one of them to true.
Symptom: Installing a second Polaris release in the same org fails with The resource 'my-polaris-db-credentials' cannot be updated because it is being managed by a different release, and nothing is created.Cause: Secret names are organization-wide, and both releases try to create the metastore credentials secret under the default name.Fix: Give the new release its own postgres.config.credentialsSecretName (or postgresHA.config.credentialsSecretName in HA mode). The first release is unaffected.
Symptom: The token endpoint rejects the CLIENT_ID and CLIENT_SECRET now in your root credentials secret.Cause: Root credentials are applied only when the realm is first bootstrapped. Changing the secret afterwards does not change the root principal.Fix: Use the credentials the realm was bootstrapped with, and create additional principals through the management API.
Symptom: The management API lists no catalogs after an upgrade.Cause: realm was changed, so Polaris bootstrapped and now serves a new, empty realm.Fix: Set realm back to its original value and upgrade again; the original catalogs reappear.
Symptom: Creating a table or committing data fails with an object-storage access error, while the catalog itself was created.Cause: storage.credentialsSecretName is empty, the secret lacks the keys AWS_ACCESS_KEY_ID and AWS_SECRET_ACCESS_KEY, or an S3-compatible catalog was created without pathStyleAccess and stsUnavailable.Fix: Set storage.credentialsSecretName to a secret with those exact keys, as in Building a Lakehouse, and recreate the catalog with the S3-compatible settings.
Symptom: helm upgrade stops at render with config.username was REMOVED in postgres 3.4.0, the `postgres` block was REMOVED in 2.5.0, or a backup.minio.accessKey and backup.minio.secretKey were REMOVED message.Cause: Your values predate template version 1.1.0, 1.2.0 or 1.2.1 respectively.Fix: Follow Upgrading from 1.0.x, Upgrading from 1.1.0 or Upgrading from 1.2.0. Do not create a metastore credentials secret yourself; this chart creates it.

Important Notes

  • Create both prerequisite secrets before installing, or the workloads wedge silently waiting on a secret that does not exist.
  • Change postgres.credentials.password before installing. The default is a published placeholder, and it is the metastore password in both modes.
  • realm is permanent. Renaming it hides every existing catalog behind a new, empty realm.
  • Root credentials are write-once. 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.
  • No credential vending. Polaris and each query engine hold their own object-storage credentials; keep iceberg.rest-catalog.vended-credentials-enabled=false in Trino.
  • Polaris does not migrate its own database schema. Treat a future Polaris version change as an explicit schema step, not something startup handles.
  • Uninstall deletes the metastore and every catalog definition with it. Enable backups or use postgresHA for anything you care about.

External References

Apache Polaris Documentation

Official documentation for the shipped release

Configuration Reference

Every server setting and its environment-variable name

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