Kubernetes Installation
This page guides you through the process of installing PaletteAI on any Kubernetes cluster, whether it is managed by cloud providers (EKS, GKE, AKS) or self-managed in on-premises or edge environments. The deployment method covered uses the hub-as-spoke pattern, which allows the hub cluster to also act as a spoke cluster, and deploys Zot for the Open Container Initiative (OCI) registry. By acting as a spoke cluster, AI/ML applications can be deployed directly on the hub cluster. To learn more about hub and spoke clusters, refer to our Hub-Spoke Model guide.
Prerequisites
To install PaletteAI, you must have an existing Kubernetes cluster and the ability to install the Mural Helm chart, which is hosted publicly via AWS ECR. An external OCI registry is recommended for production deployments but is not required.
-
The following requirements apply to the machine where you are preparing the Helm chart values and initiating the installation from:
-
The following requirements apply to your existing Kubernetes cluster on which PaletteAI will be installed:
-
Cluster admin rights
-
Kubernetes version >= 1.32.0
-
The following resources available:
-
3388m CPU
-
2732 Mi Memory
-
10Gi Storage
-
-
The Kubernetes API server must trust Dex as an identity provider. Dex is deployed as a part of the PaletteAI installation. Learn more about configuring the Kubernetes API server to trust Dex by reviewing the Configure Kubernetes API Server to Trust OIDC Provider guide.
-
A load balancer implemented for Kubernetes Ingress resources, such as:
-
Cloud provider load balancers (AWS ALB/NLB, GCP Load Balancer, Azure Load Balancer)
-
On-premises solutions (MetalLB, HAProxy, F5)
-
Other load balancer controllers compatible with Kubernetes Ingress
-
-
Installation
-
Download the latest Helm chart values file. This example uses
curl.curl --output values.yaml --silent https://docs.palette-ai.com/resources/assets/hosted/helm/values.yaml -
Open the Helm chart values file in a text editor of your choice and complete the following sections. This example uses
vi.vi values.yaml
-
Determine if you need to disable any of the open-source components, such as Ingress Nginx. PaletteAI requires these components, but if your cluster comes with one of the components already installed, you may use your existing instance. For example, some Kubernetes management platforms include Ingress Nginx with every cluster. In this case, disable Ingress Nginx by setting
ingress-nginx.enabled: falseso that PaletteAI can use the existing instance.
global
-
The
globalconfiguration is used to configure overarching settings for the PaletteAI deployment. Review and modify the following values as necessary.-
Set
global.dns.domainto the primary domain for the deployment. Do not include a protocol. For example, useexample.org, nothttps://example.org.global:
dns:
domain: 'example.acme.org' -
If you are not planning on using the ingress-nginx controller, set
global.dns.rootIngress.enabledtofalse.global:
dns:
rootIngress:
enabled: false -
In
global.auditLogging.basicAuth, change the defaultusernameandpasswordfor audit logging. The session secret is used for encoding and decoding the PaletteAI session cookie. Credentials are not stored in the browser. The cookie is used to map the session to the user so that the server can retrieve the user's credentials.global:
auditLogging:
basicAuth:
username: REPLACE_WITH_YOUR_USERNAME
password: REPLACE_WITH_YOUR_PASSWORDComplete
globalconfiguration section
-
alertmanager
-
Navigate to the
alertmanagersection. Update credentials for thealertmanagerinstance based on the credentials you configured in theglobalsection.You must provide a Base64 encoded string for the
Authorizationheader. Use the interactive encoder below to generate your Base64 encoded string and copy the value to the clipboard.Base64 Encoded String:Alternatively, generate the Base64 encoded string using the following command. Replace
usernameandpasswordwith the username and password you configured in theglobalsection.echo -n "username:password" | base64Following is the
livenessProbeandreadinessProbesections with the Base64 encoded string. ReplaceREPLACE_WITH_YOUR_BASE64_ENCODED_STRINGwith the Base64 encoded string you generated.alertmanager:
livenessProbe:
httpGet:
path: /-/healthy
port: http
scheme: HTTPS
httpHeaders:
- name: Authorization
value: 'Basic REPLACE_WITH_YOUR_BASE64_ENCODED_STRING'
readinessProbe:
httpGet:
path: /-/ready
port: http
scheme: HTTPS
httpHeaders:
- name: Authorization
value: 'Basic REPLACE_WITH_YOUR_BASE64_ENCODED_STRING'Complete
alertmanagerconfiguration section
dex
-
Dex authenticates users to PaletteAI through SSO. You can configure Dex to connect to an upstream OIDC provider or to a local user database. For this installation, you will configure Dex to connect to an upstream OIDC provider. If you want to configure an OIDC provider later, you can do so; however, Dex still requires some basic configuration.
a. Set
dex.config.issuerto your domain. Do not remove the/dexpath.dex:
config:
issuer: 'https://replace.with.your.domain/dex'b. This next part may be deferred for later, but we strongly recommend configuring at least one connector. Set the
dex.config.connectorsto the connectors you want to use. The Dex documentation has examples for each of the connectors.Below is an example of an OIDC connector that connects to AWS Cognito. The
oidctype can be used for any OIDC provider that does not have a native Dex connector. Different OIDC providers may require different configurations.Example AWS Cognito configurationdex:
config:
connectors:
- type: oidc
id: aws
name: AWS Cognito
config:
issuer: https://cognito-idp.us-east-1.amazonaws.com/us-east-1_xxxxxx
clientID: xxxxxxxxxxxxxxx
clientSecret: xxxxxxxxxxxxxxxxx
redirectURI: https://replace.with.your.domain/dex/callback # Dex's callback url for authorized code flow that will redirect to our application's callback url
getUserInfo: true
userNameKey: email
insecureSkipEmailVerified: true
insecureEnableGroups: true
scopes:
- openid
- email
- profile
promptType: consent
claimMapping:
groups: groupsc. Once you have configured the connectors, proceed to the
dex.config.staticClientssection. ReplaceREPLACE_WITH_A_UNIQUE_STRINGwith a unique string andreplace.with.your.domainwith your domain. Do not remove the/ai/callbackor/callbackpaths.dex:
config:
staticClients:
- id: mural
redirectURIs:
- 'https://replace.with.your.domain/ai/callback'
name: 'mural'
secret: 'REPLACE_WITH_A_UNIQUE_STRING'
public: false
trustedPeers:
- kubernetes
- id: kubernetes
redirectURIs:
- 'https://replace.with.your.domain/callback'
name: kubernetes
secret: 'REPLACE_WITH_A_UNIQUE_STRING'
public: false
trustedPeers:
- mural-
Next, configure the
dex.config.staticPasswordssection. We strongly recommend changing the default user and password to strong values. The following example is the default user and password in bcrypt format. Remember to use a bcrypt hash generator to generate the password hash. TheuserIDcan be any unique string.warningIf you did not configure any OIDC connectors, you must configure at least one static user, which is used to access the PaletteAI UI. Static Dex users automatically inherit admin privileges through the service account. Dex does not support groups for local static users. To use groups for local static users, you must use the User Impersonation feature.
dex:
config:
staticPasswords:
- email: 'admin@example.com'
hash: '$2a$12$Ot2dJ0pmdIC2oXUDW/Ez1OIfhkSzLZIbsumsxkByuU3CUr02DtiC.'
username: 'admin'
userID: '08a8684b-db88-4b73-90a9-3cd1661f5466' -
Lastly, configure the
dex.ingresssection to expose Dex. Forhost, replacereplace.with.your.domainwith your domain. Do not change theclassNameor thepath. Because TLS is terminated at the load balancer, thetlssection is empty.dex:
ingress:
enabled: true
className: 'nginx'
annotations: {}
hosts:
- host: replace.with.your.domain
paths:
- path: /dex
pathType: ImplementationSpecific
tls: []Complete
dexconfiguration section
-
canvas
-
Canvas controls part of the user interface. Review and modify the following values as necessary.
-
Set
canvas.ingress.enabledtotrue. Set thecanvas.ingress.domainto your domain, omitting the protocol.canvas:
ingress:
enabled: true
annotations: {}
ingressClassName: nginx
domain: example.acme.org
tls: []
paths:
- path: /ai
pathType: ImplementationSpecific
backend:
service:
name: canvas
port:
number: 2999 -
Set
canvas.enableHTTPtotrue. This supports TLS termination at the load balancer.canvas.ingress.tlsremains empty as a result.canvas:
enableHTTP: true -
The last portion of the Canvas configuration is the OIDC configuration. If you deferred configuring OIDC for Dex, you may do the same for Canvas configure it later.
In the
canvas.oidcsection, enter a unique string for thesessionSecret. ForredirectURL, replacereplace.with.your.domainwith your domain. Do not remove the/ai/callbackpath.Set
skipSSLCertificateVerificationtotrueto allow self-signed certificates generated by cert-manager. If you configured the ClusterIssuer to use a Certificate Authority (CA) provided by your organization, setskipSSLCertificateVerificationtofalse.canvas:
oidc:
sessionSecret: 'REPLACE_WITH_A_UNIQUE_STRING'
sessionDir: '/app/sessions'
issuerK8sService: 'https://dex.mural-system.svc.cluster.local:5554/dex'
skipSSLCertificateVerification: true
redirectURL: 'https://replace.with.your.domain/ai/callback'If you did not configure your Kubernetes cluster to trust Dex as an OIDC provider and do not plan to, then you must configure the
impersonationProxysection to enable user impersonation. The example below shows how to configure the local Dex useradmin@example.comto be mapped to an example Kubernetes groupadmin. Review our User Impersonation page to learn more about how to configure user impersonation for OIDC groups and other use cases.canvas:
impersonationProxy:
enabled: true
userMode: passthrough
userMap: {}
groupsMode: map
groupMap: {}
dexGroupMap:
'admin@example.com': [ 'admin' ]Complete
canvasconfiguration section
-
ingress-nginx
-
If your cluster already includes Ingress Nginx, set
ingress-nginx.enabledtofalse.ingress-nginx:
enabled: falseOtherwise, configure
ingress-nginx.controllerto use the HTTP listener and terminate TLS at the load balancer. Change the value fortargetPorts.httpstohttp.ingress-nginx:
controller:
service:
targetPorts:
http: http
https: httpLoadBalancer Service AlternativeInstead of using Ingress Nginx, you can disable
ingress-nginxentirely and configure Canvas to use a LoadBalancer service type instead. To use this method, set the following values in your Helm chart:ingress-nginx.enabled: falsecanvas.ingress.enabled: falsecanvas.service.type: LoadBalancer
Complete
ingress-nginxconfiguration section
flux2
-
The last portion of the
values.yamlfile to update is theflux2section.Set
flux2.policies.createtofalseto disable the Flux2 network policies. These policies, if enabled, prevent ingress traffic from reaching their target services.flux2:
policies:
create: falseinfoThis step is not required if the hub and all spoke clusters are configured to use a common, external OCI registry. An external OCI registry is configured in the
fleetConfig.spokes[*].ociRegistryandhue.ociRegistrysections of thevalues.yamlfile.Complete
flux2configuration section
cert-manager
-
Install cert-manager before installing the mural-crds and Mural Helm charts. Palette AI requires cert-manager to automatically provision and manage Transport Layer Security (TLS) certificates in Kubernetes.
Refer to the cert-manager documentation for more information.
Add the cert-manager Helm repository.
helm repo add jetstack https://charts.jetstack.io
helm repo update jetstackInstall cert-manager.
helm install cert-manager jetstack/cert-manager \
--namespace cert-manager \
--create-namespace \
--version v1.19.1 \
--set crds.enabled=true \
--waitVerify that cert-manager is running.
kubectl get pods -n cert-managerYou should observe output similar to the following.
NAME READY STATUS RESTARTS AGE
cert-manager-7d9f7c8c7d-xz4qw 1/1 Running 0 1m
cert-manager-cainjector-5d9c8c7d-abc12 1/1 Running 0 1m
cert-manager-webhook-6b8c7d9f-def34 1/1 Running 0 1mFIPS-Compliant Installation
For environments requiring FIPS compliance, create a
cert-manager-values.yamlfile with FIPS-approved images.crds:
enabled: true
image:
repository: us-docker.pkg.dev/palette-images-fips/palette/spectro-cert-manager/cert-manager-controller
tag: v1.19.1
acmesolver:
image:
repository: us-docker.pkg.dev/palette-images-fips/palette/spectro-cert-manager/cert-manager-acmesolver
tag: v1.19.1
cainjector:
image:
repository: us-docker.pkg.dev/palette-images-fips/palette/spectro-cert-manager/cert-manager-cainjector
tag: v1.19.1
startupapicheck:
image:
repository: us-docker.pkg.dev/palette-images-fips/palette/spectro-cert-manager/cert-manager-startupapicheck
tag: v1.19.1
webhook:
image:
repository: us-docker.pkg.dev/palette-images-fips/palette/spectro-cert-manager/cert-manager-webhook
tag: v1.19.1Install cert-manager using the values file.
helm install cert-manager jetstack/cert-manager \
--namespace cert-manager \
--create-namespace \
--version v1.19.1 \
--values cert-manager-values.yaml \
--wait
Helm Install
-
Install the mural-crds chart. This chart contains the Custom Resource Definitions (CRDs) required by PaletteAI and must be installed before the PaletteAI Mural chart.
helm install mural-crds oci://public.ecr.aws/mural/mural-crds:0.3.2 \
--namespace mural-system --create-namespace --waitExample outputNAME: mural-crds
LAST DEPLOYED: Tue May 27 09:34:33 2025
NAMESPACE: mural-system
STATUS: deployed
REVISION: 1Next, install PaletteAI using the Mural Helm chart and the
values.yamlfile you configured in the previous steps.helm install mural oci://public.ecr.aws/mural/mural:0.6.2 \
--namespace mural-system --create-namespace --values values.yaml --waitExample outputNAME: mural
LAST DEPLOYED: Tue May 27 09:39:48 2025
NAMESPACE: mural-system
STATUS: deployed
REVISION: 1
DNS
-
Once PaletteAI is deployed, fetch the URL of the load balancer the ingress-nginx controller deployed.
kubectl get service ingress-nginx-controller --namespace mural-systemExample outputNAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
ingress-nginx-controller LoadBalancer 10.104.129.101 a9d221d65b2fd41b3929574458e8ce05-1177779699.us-east-1.elb.amazonaws.com 80:31952/TCP,443:30926/TCP 41m - Create a DNS record pointing the domain configured in the Mural chart values to the load balancer created by the Ingress Nginx Service. Use an A record for IP addresses or a CNAME/alias record for hostnames, depending on your DNS provider's capabilities.
You have now deployed PaletteAI on your Kubernetes cluster. The cluster is configured to trust Dex as an identity provider. Assuming you have configured Dex with an OIDC connector, you can now log in to PaletteAI using your identity provider. Alternatively, you can use the default Dex local user to log in to PaletteAI.
If you need to make changes to your installation, review the Helm Chart Configuration Reference page and modify your values.yaml file as necessary. To trigger the update, use the following command.
helm upgrade mural oci://public.ecr.aws/mural/mural:0.6.2 \
--namespace mural-system --values values.yaml --wait
Proceed to the Validate section to validate that PaletteAI is deployed and configured correctly.
Validate
Take the following steps to verify that PaletteAI is deployed and configured correctly.
-
Open a browser and navigate to the domain URL you configured for PaletteAI.
-
Login with the default username and password. If you configured Dex with an OIDC connector, log in with your identity provider.
Next Steps
Once PaletteAI is installed on your cluster, you must integrate Palette with PaletteAI using PaletteAI's Settings resource. This resource requires a Palette tenant, project, and API key in order to communicate with Palette and deploy AI/ML applications to the appropriate location.
Proceed to the Integrate with Palette guide to learn how to prepare your Palette environment.