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 thereplicas knob scales the catalog horizontally with nothing else to coordinate.
What Gets Created
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.Create the root credentials secret
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.rootCredentials.secretName to this name.Create the token signing key secret
tokenSigningKey.secretName to this name.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
CLI
Terraform
Pulumi
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 (minimum1; there is no request-based autoscaling). See Scaling and Availability.resources— Per replica.minMemorymay not exceedmaxMemory, andmaxCpumay not exceed four timesminCpu; the chart refuses to render otherwise and names the value to fix.
JVM
resources.maxMemory (allowed range 40–80), so maxMemory is normally the only number you change.
Realm
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.
Credentials
rootCredentials.secretName— The prerequisite dictionary secret holdingCLIENT_IDandCLIENT_SECRET. Required.tokenSigningKey.secretName— The prerequisite opaque secret whose payload is the shared token signing key. Required.
Object Storage
storage.credentialsSecretName— Name of a dictionary secret holdingAWS_ACCESS_KEY_IDandAWS_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.
Bootstrap
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
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 ofpostgres 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 thepostgresblock, 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.
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
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 frompostgres.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.
Connecting
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.Prepare a bucket and its credentials
AWS_ACCESS_KEY_ID and AWS_SECRET_ACCESS_KEY. Only S3 and S3-compatible storage are supported by this template.- SeaweedFS
- MinIO
- AWS S3
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.cpln helm upgrade, using the same chart version and your other values.Get an access token
TOKEN.Create a catalog
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.Point Trino at it
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.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 withpostgres.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.
- AWS S3
- Google Cloud Storage
- S3-compatible (MinIO)
Create a bucket
backup.aws.bucket and backup.aws.region to match.Set up a Cloud Account
backup.aws.cloudAccountName to its name.Create a bucket-scoped IAM policy
YOUR_BUCKET), then set backup.aws.policyName to the policy’s name:Restoring a Backup
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:- Keep
realmidentical to the value the backup was taken under, or the restored catalogs stay invisible. - 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-polarisso every replica reconnects.
Upgrading from 1.0.x
Apply every subsection from your version onward, in order, before runningcpln 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:
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.
Upgrading from 1.1.0
Version1.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:
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: version1.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; withreplicas: 2or 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.postgresHAadds 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
Workloads never start and cpln logs returns nothing
Workloads never start and cpln logs returns nothing
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.Bootstrap logs that CLIENT_ID or CLIENT_SECRET may not contain a comma
Bootstrap logs that CLIENT_ID or CLIENT_SECRET may not contain a comma
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.Install fails with enable exactly one metastore
Install fails with enable exactly one metastore
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.Second release fails with managed by a different release
Second release fails with managed by a different release
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.Token request returns 401 after changing the root credentials
Token request returns 401 after changing the root credentials
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.Catalogs disappeared after an upgrade
Catalogs disappeared after an upgrade
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.Catalog operations fail to read or write the bucket
Catalog operations fail to read or write the bucket
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.Upgrade fails with a REMOVED key message
Upgrade fails with a REMOVED key message
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.passwordbefore installing. The default is a published placeholder, and it is the metastore password in both modes. realmis 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=falsein 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
postgresHAfor anything you care about.