Skip to main content

Customize the Helm chart

The chart installs a working deployment with its defaults, and most sites change a few of them to fit their cluster: a registry mirror, an existing database, their ingress controller, a storage class, or a pod security policy. Every change on this page goes in the my-values.yaml you installed with and takes effect when you run the install command from Install again. Check the render first, as in step 6 of the install, so a value the chart rejects fails before anything changes.

How do ISection
Pull images from my own registry?Pull images from your own registry
Use a database I already run?Use your own metadata database
Use my ingress controller, or the Gateway API?Publish through your ingress controller
Expose a service without an ingress controller?Expose a Service directly
Give a workload more CPU or memory?Size the workloads
Choose storage classes and volume sizes?Choose storage classes and volume sizes
Run under the restricted Pod Security Standard or OpenShift?Run under restricted pod security
Leave out OpenSearch, lineage, MCP, or Lance REST?Turn off optional components
Restrict network traffic to the server?Restrict network traffic
Give the server longer to start?Tune health checks
Run workloads on dedicated nodes?Place workloads on node pools
Add labels, annotations, volumes, sidecars, or CA certificates?Labels and annotations, Extra volumes and containers, Trust your own certificate authorities
Know which Kubernetes permissions the chart grants?Kubernetes permissions
Know what cannot be changed?Fixed in this release

Pull images from your own registry​

Copy the release images into your registry with the offline toolkit, then set global.registry to that registry's host. The chart rewrites every image to it except Fluent Bit. See Air-gapped installation for the steps and the pull Secret settings.

Use your own metadata database​

Set gravitino.postgresql.enabled: false and point the server at a PostgreSQL or MySQL database you operate. See Metadata database.

Publish through your ingress controller​

The chart publishes every host through one Ingress, set under ingress: the UI and MCP on ingress.hosts, and the server API, Iceberg REST, Lance REST, SCIM and an MCP host of its own on ingress.api, ingress.iceberg, ingress.lance, ingress.scim and ingress.mcp. Set ingress.className to your controller's class, as described in Published hosts.

The server API host can be published through the Gateway API instead, as an HTTPRoute attached to a Gateway you run. Leave ingress.api off when you do:

my-values.yaml
gravitino:
ingress:
enabled: true
type: gateway
gateway:
parentRefs:
- name: "{gateway_name}"
namespace: "{gateway_namespace}"
hosts:
- host: "{api_host}"
paths:
- path: /
pathType: PathPrefix

The UI, Iceberg REST, and Lance REST hosts are published only through an Ingress in this release.

Expose a Service directly​

Without an ingress controller, give a Service a type of LoadBalancer or NodePort:

ServiceValuePorts
Servergravitino.service.type8090 for the API, 9001 for Iceberg REST, 9101 for Lance REST
UIenterpriseWeb.service.type3000

gravitino.service.loadBalancerSourceRanges limits which addresses reach a LoadBalancer server Service. The Services carry plain HTTP, so traffic is unencrypted unless your load balancer terminates TLS in front of them; HTTPS publishes through an ingress controller instead.

Size the workloads​

Each workload takes standard Kubernetes resources. The defaults are in Workloads and resources.

WorkloadValues
Servergravitino.resources, with the heap in gravitino.env GRAVITINO_MEM
PostgreSQLgravitino.postgresql.resources
UIenterpriseWeb.resources
Metrics servicemetrics.resources
MCP servermcp.resources
OpenSearchopensearch.resources, with the heap in opensearch.opensearchJavaOpts
Lineage APImarquez.marquez.resources

Raise the server's memory and its heap together: the chart refuses a GRAVITINO_MEM that does not fit in gravitino.resources.limits.memory. See step 5 of Install for the values the install sets.

Choose storage classes and volume sizes​

VolumeSizeStorage class
Server logsgravitino.logPersistence.sizegravitino.logPersistence.storageClassName
Metrics service logsmetrics.logPersistence.sizemetrics.logPersistence.storageClassName
PostgreSQL datagravitino.postgresql.persistence.sizeThe cluster's default StorageClass
OpenSearch dataopensearch.persistence.sizeopensearch.persistence.storageClass

The in-cluster PostgreSQL always uses the cluster's default StorageClass. To place it on another class, make that class the default before installing, or use your own database.

PostgreSQL and OpenSearch run as StatefulSets, whose volume claims are fixed when they are created. Set their size before the first install; a later change to it is rejected by Kubernetes. To grow an existing volume, expand the PersistentVolumeClaim itself, where the StorageClass allows expansion.

Run under restricted pod security​

The chart's own workloads already meet the restricted Pod Security Standard: they run as non-root users, drop all capabilities, block privilege escalation, and use the RuntimeDefault seccomp profile. OpenSearch adds one init container that runs as root to set the ownership of its data volume. Turn it off, and the pod's fsGroup sets the ownership instead:

my-values.yaml
opensearch:
persistence:
enableInitChown: false

Each workload's security context can be changed under its own values: gravitino.podSecurityContext and gravitino.containerSecurityContext, enterpriseWeb.securityContext, metrics.securityContext, mcp.containerSecurityContext, and opensearch.securityContext.

On OpenShift, apply the packaged openshift-values.yaml, which lets the restricted-v2 SCC assign user and group IDs; see Red Hat OpenShift.

Turn off optional components​

ComponentValuesWhat is lost
OpenSearchopensearch.enabled: false, gravitino.search.storageImpl: memory, and marquez.marquez.opensearch.enable: falseA persistent search index, and search in the lineage view; see below
Lineage (Marquez)marquez.enabled: false and gravitino.lineage.enabled: falseThe lineage view and the stored lineage history; see Lineage
MCP servermcp.enabled: falseAI agent access; see MCP server
Lance REST servicegravitino.auxService.names: "iceberg-rest" and ingress.lance.enabled: falseLance clients; see Lance REST service

Lineage also searches OpenSearch, so marquez.marquez.opensearch.enable: false turns that search off while lineage stays on; with lineage turned off as well, it is not needed. Without OpenSearch, memory holds the search index in the server's memory, so after the server restarts the index is empty until it is rebuilt, as in Restore. To use an OpenSearch cluster you already run instead, keep opensearch.enabled: false and set gravitino.search.opensearch.url, gravitino.search.opensearch.username, and gravitino.search.opensearch.existingSecret, a Secret with the password under the key password.

The chart refuses to render with gravitino.lineage.enabled left on after marquez.enabled is turned off.

Restrict network traffic​

gravitino.networkPolicy.enabled: true adds a NetworkPolicy for the server pod. Its defaults are strict:

  • Inbound, only pods in the release namespace reach the server's ports. An ingress controller in another namespace is blocked.
  • Outbound, only DNS and the in-cluster database are allowed. Your sources, object storage, identity provider, and any external metadata database are blocked.

Before turning it on, list what the server must reach in gravitino.networkPolicy.ingressRules and gravitino.networkPolicy.egressRules. Each list replaces the matching defaults rather than adding to them, so include DNS and the database in egressRules as well. The example admits an ingress controller in the ingress-nginx namespace and allows outbound DNS and HTTPS:

my-values.yaml
gravitino:
networkPolicy:
enabled: true
ingressRules:
- from:
- podSelector: {}
- namespaceSelector:
matchLabels:
kubernetes.io/metadata.name: ingress-nginx
egressRules:
- ports:
- protocol: UDP
port: 53
- protocol: TCP
port: 53
- to:
- podSelector: {}
- ports:
- protocol: TCP
port: 443

Add a rule for every other port the server reaches, such as a database's or a metastore's. The policy covers the server pod only; the other workloads have none.

Tune health checks​

The server's readiness and liveness checks are under gravitino.readinessProbe and gravitino.livenessProbe. Change only their timings, such as initialDelaySeconds or failureThreshold, and keep the check itself; the chart's liveness check also keeps the pod running when it stops for a license limit. A server with many catalogs to load at startup may need a longer initial delay:

my-values.yaml
gravitino:
readinessProbe:
initialDelaySeconds: 60
livenessProbe:
initialDelaySeconds: 180

The UI's checks are under enterpriseWeb.readinessProbe and enterpriseWeb.livenessProbe.

Place workloads on node pools​

Each workload takes the standard Kubernetes scheduling settings, nodeSelector, tolerations and affinity, under its own key in the values file:

WorkloadValues
Servergravitino.nodeSelector, gravitino.tolerations, gravitino.affinity, gravitino.topologySpreadConstraints
UI and metrics servicenodeSelector, tolerations, affinity at the top level of the values file; the two share them
MCP servermcp.nodeSelector, mcp.tolerations, mcp.affinity
Lineage API and web viewmarquez.nodeSelector, marquez.tolerations, marquez.affinity
OpenSearchopensearch.nodeSelector, opensearch.tolerations, opensearch.affinity
Registry-token renewal jobglobal.pullSecrets.ecrRefresh.nodeSelector, .tolerations, .affinity

The in-cluster metadata database and the schema Jobs that run at install and upgrade have no scheduling values in this release, so they run wherever the scheduler places them. They need at least one node without a taint they would have to tolerate, even when the metadata database is external, because the server waits for the schema Job before it starts.

The example puts every workload on a node pool labelled pool: datastrato and tainted dedicated=datastrato:NoSchedule:

my-values.yaml
nodeSelector:
pool: datastrato
tolerations:
- key: dedicated
operator: Equal
value: datastrato
effect: NoSchedule

gravitino:
nodeSelector:
pool: datastrato
tolerations:
- key: dedicated
operator: Equal
value: datastrato
effect: NoSchedule

mcp:
nodeSelector:
pool: datastrato
tolerations:
- key: dedicated
operator: Equal
value: datastrato
effect: NoSchedule

marquez:
nodeSelector:
pool: datastrato
tolerations:
- key: dedicated
operator: Equal
value: datastrato
effect: NoSchedule

opensearch:
nodeSelector:
pool: datastrato
tolerations:
- key: dedicated
operator: Equal
value: datastrato
effect: NoSchedule

The server needs a node with 8Gi of memory free for its request; see the workloads and resources table in Install.

Labels and annotations​

Pod annotations carry settings for tools that read them, such as a service mesh, a log collector or a backup operator. Pod labels let policies and monitoring select the pods.

WorkloadPod annotationsPod labels
Servergravitino.podAnnotationsgravitino.podLabels
UIenterpriseWeb.podAnnotations
Metrics servicemetrics.podAnnotations
MCP servermcp.podAnnotations
Lineage APImarquez.marquez.podAnnotations

To give the server's Kubernetes service account a cloud identity, such as an IAM role on EKS or a Google service account on GKE, annotate it with gravitino.serviceAccount.annotations.

Extra volumes and containers​

The server takes extra volumes, containers and init containers:

ValueAdds
gravitino.extraVolumesVolumes on the server pod
gravitino.extraVolumeMountsMounts of those volumes in the server container
gravitino.extraInitContainersContainers that run before the server starts
gravitino.extraContainersContainers that run beside the server, such as a log shipper

The chart refuses to render a mount of a Secret that does not exist yet, so create the Secret first. /opt/gravitino/logs is always the chart's logs volume and cannot be remounted. The Google Cloud credentials section shows a complete example. The MCP server takes mcp.extraVolumes and mcp.extraVolumeMounts the same way.

Trust your own certificate authorities​

When the server connects to a database, metastore, identity provider or other service whose certificate is signed by an internal certificate authority, import that authority into the server's Java trust store:

Store the CA certificates in a Secret
kubectl -n "$NAMESPACE" create secret generic internal-ca --from-file=ca.crt=./internal-ca.crt
my-values.yaml
gravitino:
ssl:
trustStore:
enabled: true
existingSecrets:
- name: internal-ca

Every .crt and .pem file in each Secret listed is imported. existingSecret takes a single Secret instead. ssl.trustStore.javaOptions adds Java system properties for TLS, such as -Djavax.net.debug=ssl:handshake while troubleshooting a handshake.

The UI makes its own TLS connections, to your identity provider for sign-in. When the identity provider's certificate comes from an internal authority, mount the same Secret into the UI and point Node.js at it:

my-values.yaml
enterpriseWeb:
volumes:
- name: internal-ca
secret:
secretName: internal-ca
volumeMounts:
- name: internal-ca
mountPath: /etc/internal-ca
readOnly: true
env:
- name: NEXTAUTH_URL
value: "https://{ui_hostname}"
- name: AUTH_TRUST_HOST
value: "true"
- name: NODE_EXTRA_CA_CERTS
value: /etc/internal-ca/ca.crt

enterpriseWeb.env replaces the chart's list, so keep the NEXTAUTH_URL and AUTH_TRUST_HOST entries from your my-values.yaml with the values you installed with. The UI's session secret is not in this list: the chart keeps it in the Secret {release}-ui-auth.

Environment variables​

Environment variables for the server go in gravitino.env, and for the UI in enterpriseWeb.env. See Configuration files and environment.

Kubernetes permissions​

The chart creates no cluster-wide roles. Each Role it creates is limited to the release namespace:

Service accountCan
UIRead and update the server ConfigMap; read and update Deployments and StatefulSets; read pods, Ingresses, and Istio Gateways
Schema JobScale the server Deployment and read pods, during install and upgrade
Registry-token renewalCreate and update the image pull Secret, when global.pullSecrets.ecrRefresh is on

Where the installer may not grant access to Istio Gateways, set enterpriseWeb.rbac.istioGateway: false.

Fixed in this release​

  • One server replica. The server runs as a single pod and does not scale out or autoscale.
  • A disruption budget blocks drains. gravitino.podDisruptionBudget with its default minAvailable: 1 keeps a node with the server pod from draining, since one replica can never be disrupted. Leave it off.
  • PostgreSQL storage class. The in-cluster PostgreSQL uses the cluster's default StorageClass.