Configuration
The settings you choose at install time: the fields of the Olympus resource for an operator install, and the Helm chart values for a chart install. Both reach the same chart, so a setting does the same thing on either path.
The Olympus resource
Section titled “The Olympus resource”The resource is a curated set of decisions, not a copy of the chart’s values: where the platform is reachable, which database, who signs the tokens, where the secrets are, and which optional parts run. Everything else keeps the chart’s default. oc explain olympus.spec --recursive (or kubectl explain) lists every field with its documentation. The kind is Olympus in olympus.olympusx.ai/v1; its short name is olymp.
Four blocks are required: ingress, auth, secrets and database. The API server refuses a resource without them.
ingress
Section titled “ingress”| Field | Default | What it does |
|---|---|---|
consoleHost |
Required | The console’s hostname. Sign-in is served on it under /auth |
gatewayHost |
Required | The OpenAI-compatible API’s hostname |
className |
nginx |
The IngressClass. The cluster’s default IngressClass is not consulted. Ignored on OpenShift, which gets Routes |
tlsSecret |
Empty | An existing TLS Secret covering both hosts. Empty keeps the names olympus-console-tls and olympus-gateway-tls. Create those Secrets yourself, or let cert-manager issue into them. Ignored on OpenShift, where the Route terminates TLS |
| Field | Default | What it does |
|---|---|---|
issuer |
Required | The OIDC issuer, exactly as tokens carry it, with no trailing slash. With the built-in identity service, https://<console-host>/auth/realms/olympus |
clientId |
olympus-studio |
The client the console signs in as |
secrets
Section titled “secrets”Every entry is the name of a Secret in the same namespace, never a value.
| Field | Default | What it does |
|---|---|---|
masterKey |
Required | A Secret with the entry SECRETS_MASTER_KEY: base64 of 32 random bytes. The operator refuses to install when the Secret or the entry is missing |
registry |
Empty | The image pull Secret for the platform’s images. Needed on every install that pulls from the subscription registry |
database
Section titled “database”| Field | Default | What it does |
|---|---|---|
mode |
Required | managed: PostgreSQL run by the PostgreSQL operator in this namespace. external: your own server |
instances |
3 |
Managed instances. 1 is allowed for evaluation and is not highly available |
storageSize |
20Gi |
Each instance’s data volume |
storageClass |
Cluster default | Must support ReadWriteOnce |
walStorageSize |
10Gi |
Each instance’s separate write-ahead-log volume, so the log cannot fill the data disk |
walStorageClass |
Cluster default | |
backupDestination |
Empty | An s3://bucket/path for continuous archiving. Without it there are no backups, and deleting the resource deletes the database with no copy anywhere |
backupEndpoint |
AWS S3 | The S3 endpoint. Required for every other store (on-premises object storage, OCI Object Storage); without it archiving fails while the resource reports Ready |
backupCredentialsSecret |
olympus-backup-s3 |
A Secret with ACCESS_KEY_ID and ACCESS_SECRET_KEY. You create it; nothing deletes it |
backupSchedule |
0 30 2 * * * (02:30 daily) |
When a full base backup runs. Six fields: the first is seconds. The API server refuses five |
archiveName |
The database cluster’s name | The folder this install archives into. Once set, leave it set |
urlSecret |
Empty | external only, and required there: a Secret with DATABASE_URL |
appRoleSecret |
Empty | A Secret with a connection string for the restricted application role. Required by components.tenantIsolation |
appRoleSecretKey |
DATABASE_URL |
The entry inside appRoleSecret |
restoreFrom |
Empty | Brings a new install up from a previous install’s archive: sourceServerName (required: the database cluster that wrote the archive, <name>-db for a resource named <name>), destination, pointInTime. Setting it on a running install changes nothing. Refused with external. Backup and restore |
A schedule without a destination does nothing: set backupDestination, backupEndpoint, backupCredentialsSecret and backupSchedule together.
components
Section titled “components”Every optional part here is off unless you turn it on.
| Field | Default | What it does |
|---|---|---|
keycloak |
false |
Deploys the built-in identity service, which then must be the auth.issuer. Leave off only when the issuer is your own provider |
keycloakBootstrapUser |
admin@olympusx.ai |
The first platform administrator: an email address, which is also the username. Used once, at first start |
keycloakSmtp |
Unset | The relay invitations go through: host (no scheme, no port), port (587), from, fromDisplayName (Olympusx AI), encryption (starttls, ssl or none), and auth with username, passwordSecret and passwordSecretKey (password). Applied at the first install only |
keycloakRealm |
olympus |
The realm the identity service creates and issues tokens for |
keycloakRelativePath |
/auth |
The path the identity service is served under. Must be a path segment |
keycloakHostname |
Empty | A hostname of its own for the identity service, scheme included, ending in the same path, such as https://sso.example.com/auth. Empty serves it on the console’s host |
keycloakJdbcUrl |
Empty | Where the identity service keeps its state. Required with the identity service on and database.mode: external |
guardrails |
false |
Runs the guardrails service in the cluster. Requires guardrailsImage |
guardrailsImage |
Empty | The scanner’s digest (sha256:...) as the release pins it, never a full image reference |
externalGuardrailsUrl |
Empty | A scanner you already run, with an http:// or https:// scheme. Excludes guardrails: true. It also turns on review capture for the gateway |
langfuse |
false |
Runs the tracing store, which the console reads traces from. Adds a 50Gi volume |
langfuseHostname |
Empty | A browser address for the tracing store’s own interface. Empty leaves it reachable by port-forward only |
monitoring |
false |
ServiceMonitors and alert rules. Needs the Prometheus Operator’s CRDs |
tenantIsolation |
false |
The control plane connects as the restricted application role. Requires database.appRoleSecret |
With the built-in identity service on, the issuer is https://<console-host><keycloakRelativePath>/realms/<keycloakRealm>, or <keycloakHostname>/realms/<keycloakRealm> with a hostname. Any other auth.issuer is refused with RenderFailed, naming both issuers.
modelServing
Section titled “modelServing”| Field | Default | What it does |
|---|---|---|
provider |
kubernetes |
kubernetes: the control plane runs each deployment as its own model server, on GPUs or on CPU nodes. kubernetes-operator: it writes a ModelDeployment for the operator to reconcile |
namespace |
olympus-serving |
The shared namespace for model workloads. Project namespaces are separate |
importRegistry |
Empty | The registry imported model weights are pushed to. Empty turns in-cluster model import off, and the console says so |
agents, networkPolicy, clusterRegistration, image, version
Section titled “agents, networkPolicy, clusterRegistration, image, version”| Field | Default | What it does |
|---|---|---|
agents.mcpInternalAllowList |
Empty | In-cluster hosts and networks a tenant’s tool servers may be on: host names, *. wildcards, addresses and CIDRs. Empty allows public https:// addresses only. Loopback, link-local and cloud metadata addresses are never reachable |
networkPolicy.metricsScrapers |
The monitoring namespace; on OpenShift, its monitoring namespaces |
NetworkPolicy peers allowed to scrape metrics. The list replaces the default |
networkPolicy.cnpgOperatorNamespace |
The PostgreSQL operator’s default namespace; openshift-operators on OpenShift |
Where the PostgreSQL operator runs. Wrong here, the database still serves but stops failing over |
clusterRegistration.enabled |
true |
Registers this cluster with the platform at start-up |
clusterRegistration.name |
in-cluster |
The cluster’s name in the console |
image.registry |
The subscription registry | Where the platform’s own images come from. No trailing slash |
version |
The version the operator ships with | Pins the platform version. Set it only to hold a version back |
Keep agents.mcpInternalAllowList narrow. The cluster’s service and pod networks contain the platform’s own services; listing them gives a tenant administrator the reach the policy exists to remove.
Cluster registration grants a credential. It creates a second ServiceAccount, <name>-agent, a non-expiring token for it, and a ClusterRole and binding, <name>-selfregister, that read nodes, pods and metrics and manage namespaces, quotas, limits, NetworkPolicies, RBAC, ServiceAccounts, Deployments, Services, ConfigMaps, Jobs, Events and Secrets, plus the model and database resources where their CRDs exist. The token is sealed with the master key. It is the same grant any other registered cluster needs (Clusters). To remove the cluster, set enabled: false first, then delete it in the console; deleting it in the console alone recreates it at the next restart with a new identity.
Not on the resource
Section titled “Not on the resource”These are chart values with no field on the resource. Where you need one, install with the chart.
modelServing.runAsUser: the user ID model servers run as. Not needed on OpenShift, which assigns one.modelServing.downloadProxy: the proxy model servers fetch weights through. Without it, a model in a project namespace loads its weights from your registry.providers.ociWorkloadIdentity: OCI Generative AI with no stored key. Connect Oracle with an API key instead.controlPlane.extraEnv: an egress proxy. Without it, the platform namespace needs direct egress to any cloud model provider.
Helm chart values
Section titled “Helm chart values”helm show values oci://<registry>/charts/olympus --version <version> prints every value with its documentation. These are the ones customers set.
Secrets and images
Section titled “Secrets and images”| Value | Default | What it does |
|---|---|---|
secrets.existingSecret |
Empty | A Secret with SECRETS_MASTER_KEY, and DATABASE_URL and the cache URL when the bundled stores are off |
secrets.masterKey |
Generated | Generated keys live only in this cluster. Prefer existingSecret |
secrets.gitops |
false |
true makes the chart refuse to generate a key. Set it under any tool that renders with helm template |
image.registry |
The subscription registry | Where the platform’s images come from |
image.pullSecrets |
Empty | Pull Secrets for the platform’s pods |
Hostnames
Section titled “Hostnames”| Value | Default | What it does |
|---|---|---|
ingress.enabled |
false |
Renders an Ingress for each host |
ingress.className |
nginx |
The IngressClass |
ingress.consoleHost, ingress.gatewayHost |
Empty | The two hostnames. consoleHost is required with an Ingress |
ingress.tls.consoleSecret, ingress.tls.gatewaySecret |
olympus-console-tls, olympus-gateway-tls |
The TLS Secrets |
ingress.annotations |
Empty | For cert-manager or your ingress controller |
route.consoleHost, route.gatewayHost |
Empty | OpenShift Routes, with the OpenShift values file. route.consoleHost is required |
gateway.publicUrl |
Derived from the gateway host | The API address developers are given, when the gateway is reached some other way |
Database
Section titled “Database”| Value | Default | What it does |
|---|---|---|
postgresql.enabled |
true |
The bundled single instance. Turn it off for the managed database or your own |
postgresqlCNPG.enabled |
false |
The managed database. Exactly one of the two may be on |
postgresqlCNPG.instances |
3 |
|
postgresqlCNPG.storage.size, .storageClass |
20Gi, cluster default |
|
postgresqlCNPG.walStorage.size, .storageClass |
10Gi, cluster default |
|
postgresqlCNPG.backup.destinationPath |
Empty | Required while postgresqlCNPG.backup.enabled is true, which is the default |
postgresqlCNPG.backup.endpointURL |
AWS S3 | Required for every other store |
postgresqlCNPG.backup.credentialsSecret |
olympus-backup-s3 |
A Secret with ACCESS_KEY_ID and ACCESS_SECRET_KEY |
postgresqlCNPG.backup.schedule.cron |
0 30 2 * * * |
Six fields; the first is seconds |
postgresqlCNPG.backup.retentionPolicy |
30d |
|
externalPostgresql.existingSecret |
Empty | A Secret with DATABASE_URL, when both bundled options are off |
Identity
Section titled “Identity”The built-in identity service’s values sit under one block, shown here with their defaults:
keycloak: enabled: true # false with your own identity provider bootstrapUser: admin@olympusx.ai # the first platform administrator realm: olympus relativePath: /auth hostname: "" # empty: served on the console's host smtp: # applied at the first install only enabled: false host: "" port: 587 from: "" fromDisplayName: Olympusx AI starttls: true # or ssl: true, for port 465 auth: enabled: false username: "" existingSecret: "" # a Secret with the relay password passwordKey: password security: # applied at the first install only passwordPolicy: "length(12) and upperCase(1) and lowerCase(1) and digits(1) and specialChars(1) and notUsername" accessTokenLifespanSeconds: 900 ssoSessionIdleTimeoutSeconds: 1800 ssoSessionMaxLifespanSeconds: 36000| Value | Default | What it does |
|---|---|---|
auth.issuer |
Derived from the console host | Your own provider’s issuer, with the built-in identity service off |
auth.audience, auth.clientId |
olympus-studio |
The aud tokens must carry, and the console’s client |
Components
Section titled “Components”| Value | Default | What it does |
|---|---|---|
guardrails.enabled |
false |
The guardrails service, at the digest the release pins |
guardrails.failOpen |
false |
Lets traffic through uninspected while the scanner is unreachable. A platform administrator can change it at runtime |
externalGuardrails.url |
Empty | A scanner you already run |
reranker.enabled |
false |
Requires reranker.image.digest |
langfuse.enabled |
false |
The tracing store |
monitoring.enabled |
false |
ServiceMonitors and alert rules |
monitoring.serviceMonitor.additionalLabels |
Empty | Labels your Prometheus’s serviceMonitorSelector requires |
monitoring.prometheusRule.additionalLabels |
The ServiceMonitor labels | Labels its ruleSelector requires |
Model serving and providers
Section titled “Model serving and providers”| Value | Default | What it does |
|---|---|---|
modelServing.provider |
kubernetes |
As on the resource |
modelServing.runAsUser |
0 (none) |
Set 1000 on every cluster other than OpenShift. Applies to model servers on every registered cluster |
modelServing.downloadProxy.enabled, .domains |
false, huggingface.co and hf.co |
A proxy that lets model servers on the install cluster reach the listed domains on port 443 and nothing else |
modelServing.import.registry |
Empty | Where imported weights are pushed. Empty turns import off |
modelServing.import.image |
Empty | The release’s model packaging image, or your mirror of it. Required for import |
modelServing.import.hfTokenSecret |
Empty | A Secret holding a Hugging Face token, for gated repositories |
providers.ociWorkloadIdentity.enabled, .region |
false, empty |
OCI Generative AI with no stored key, on an enhanced OKE cluster (Oracle OKE) |
controlPlane.extraEnv |
Empty | Environment for the control plane, such as HTTPS_PROXY and NO_PROXY for an egress proxy |
Cluster, network and placement
Section titled “Cluster, network and placement”| Value | Default | What it does |
|---|---|---|
clusterRegistration.enabled |
false |
Registers this cluster at start-up. On by default on the resource, off in the chart |
clusterRegistration.name |
in-cluster |
|
networkPolicy.enabled |
true |
Leave it on |
networkPolicy.metricsScrapers |
The monitoring namespace |
As on the resource |
networkPolicy.cnpgOperator |
The PostgreSQL operator’s default namespace | Peers for the PostgreSQL operator |
networkPolicy.gatewayFrom |
Any source | Narrows who may call the gateway: your ingress controller’s namespace and in-cluster clients |
controlPlane.agents.mcpInternalAllowList |
Empty | As agents.mcpInternalAllowList on the resource |
controlPlane.agents.maxToolResultBytes |
32768 |
The largest tool result an agent’s model reads |
controlPlane.agents.toolCallTimeoutSeconds |
60 |
One tool call’s limit |
nodeSelector, tolerations, affinity, priorityClassName, topologySpreadConstraints |
Empty | Chart-wide placement, overridable per component |
<component>.replicas, <component>.resources |
Per component | Replicas and requests |
Bring your own identity provider
Section titled “Bring your own identity provider”On the resource: leave the built-in identity service off (its default) and set auth.issuer to your provider’s issuer, and auth.clientId to the client you registered for the console. Nothing else is required: the control plane reads <issuer>/.well-known/openid-configuration at start-up.
With the chart:
keycloak: enabled: falseauth: issuer: https://<idp-host>/<issuer-path> audience: <audience> clientId: <client-id>The issuer must equal the iss your provider stamps, byte for byte, with no trailing slash; the control plane refuses to start with one. The audience must match the tokens’ aud; without the match, people sign in and every API call answers 401. An issuer that resolves to nothing does not fail the install: the platform reports healthy and nobody can sign in, and the control plane’s log says identity provider not reachable yet.
Your provider must issue the claims Olympusx AI reads, and you register the console’s client with redirect addresses on <console-host>. Identity and sign-in has the claims, the roles and the client settings.
Bring your own database
Section titled “Bring your own database”On the resource: database.mode: external and database.urlSecret naming a Secret with DATABASE_URL. With the built-in identity service also on, set components.keycloakJdbcUrl: the identity service needs a JDBC URL, which cannot be derived from a connection string.
With the chart:
kubectl -n olympus create secret generic olympus-secrets \ --from-literal=SECRETS_MASTER_KEY="$(openssl rand -base64 32)" \ --from-literal=DATABASE_URL='postgres://olympus:<password>@<db-host>:5432/olympus?sslmode=require'secrets: existingSecret: olympus-secretspostgresql: enabled: false# Only with the built-in identity service on your database:keycloak: database: jdbcUrl: jdbc:postgresql://<db-host>:5432/olympus username: olympus existingSecret: <identity-db-secret> # a Secret with the key `password`The built-in identity service needs its own connection to your database, as a JDBC URL, because one cannot be derived from a connection string. It keeps its tables in a schema of their own. Without the JDBC URL, the chart stops and names the value.
Your own cache works the same way: turn the bundled one off with redis.enabled: false and put its URL in secrets.existingSecret under REDIS_URL. It must speak the Redis protocol, version 6.2 or later. The cache holds budget counters: if it is unavailable, budgets and rate limits stop applying until it returns.
Tenant isolation
Section titled “Tenant isolation”Row-level security already separates tenants’ rows in every install. Tenant isolation adds privilege separation: the control plane connects as olympus_app, a role that can read and write rows but cannot alter tables or drop the policies. Migrations keep running as the owner.
Have a DBA create the role once, as a superuser or a role with CREATEROLE, on the platform database. Check first:
SELECT rolcanlogin FROM pg_roles WHERE rolname = 'olympus_app';If it is missing or cannot log in:
CREATE ROLE olympus_app LOGIN PASSWORD '<password>' NOSUPERUSER NOCREATEDB NOCREATEROLE NOBYPASSRLS;GRANT USAGE ON SCHEMA public TO olympus_app;GRANT SELECT, INSERT, UPDATE, DELETE ON ALL TABLES IN SCHEMA public TO olympus_app;GRANT USAGE, SELECT ON ALL SEQUENCES IN SCHEMA public TO olympus_app;ALTER DEFAULT PRIVILEGES IN SCHEMA public GRANT SELECT, INSERT, UPDATE, DELETE ON TABLES TO olympus_app;ALTER DEFAULT PRIVILEGES IN SCHEMA public GRANT USAGE, SELECT ON SEQUENCES TO olympus_app;Then the Secret:
kubectl -n olympus create secret generic olympus-app-db \ --from-literal=DATABASE_URL='postgres://olympus_app:<password>@<db-host>:5432/olympus?sslmode=require'And turn it on: components.tenantIsolation: true with database.appRoleSecret: olympus-app-db on the resource, or with the chart:
tenantIsolation: enabled: true appSecret: olympus-app-dbThe operator refuses tenantIsolation without appRoleSecret rather than reporting isolation while serving as the owner.
Monitoring
Section titled “Monitoring”With monitoring on, the chart renders two ServiceMonitors and a PrometheusRule with the platform’s alerts. Two things go wrong, differently:
-
The Prometheus Operator’s CRDs are missing. The install stops and says so.
-
Your Prometheus’s selectors do not match. Nothing fails, and nothing is scraped. Read the selectors, and set the matching labels in
monitoring.serviceMonitor.additionalLabelsand, if it differs,monitoring.prometheusRule.additionalLabels:kubectl get prometheus -A -o jsonpath='{..serviceMonitorSelector}{"\n"}{..ruleSelector}{"\n"}'
networkPolicy.metricsScrapers must also admit the namespace your Prometheus runs in. Wrong, every pod stays Ready and the targets show as down. Monitoring and alerts lists the alerts.
NetworkPolicy peers
Section titled “NetworkPolicy peers”The platform’s NetworkPolicies admit two outside parties by namespace: your Prometheus on the metrics ports, and the PostgreSQL operator on each database instance’s management port. On Kubernetes the defaults are the monitoring namespace and the PostgreSQL operator’s own default namespace; on OpenShift, OpenShift’s monitoring namespaces and openshift-operators. Set them when yours run elsewhere:
spec: networkPolicy: cnpgOperatorNamespace: <operator-namespace> metricsScrapers: - namespaceSelector: matchLabels: kubernetes.io/metadata.name: <prometheus-namespace>If the PostgreSQL operator cannot reach the instances, the database keeps serving queries, stops reporting status and stops failing over.