Skip to content

Install on OpenShift

This page installs Olympusx AI on OpenShift through the Olympusx AI operator: the catalog, the operator, and one Olympus resource that the operator turns into a running platform. Plan on about an hour, most of it waiting for images. Each step gives the oc command; where the OpenShift web console does the same thing, its path is named too.

  • OpenShift 4.20 or later, and an account with cluster-admin. The operator needs Kubernetes 1.33.

  • oc, logged in as that account.

  • linux/amd64 worker nodes for the platform.

  • Room for the platform: about 5 CPU and 12 GiB of memory in requests, before any model serving.

  • Storage: a ReadWriteOnce StorageClass for the database (three instances, each with a 20 GiB and a 10 GiB volume), and a default StorageClass for the object store’s 50 GiB volume. oc get storageclass marks the default. On OpenShift Data Foundation, use the Ceph RBD class for the database.

  • The PostgreSQL operator, installed for all namespaces (Requirements).

  • The registry credential from your subscription.

  • An SMTP relay the cluster can reach, for invitations.

  • Two hostnames directly under the apps domain. The Routes use edge TLS with the router’s default certificate, which covers one label deep. Print the apps domain:

    oc get ingresses.config/cluster -o jsonpath='{.spec.domain}{"\n"}'

    Then use, for example, olympus.<apps-domain> for the console and olympus-api.<apps-domain> for the gateway. The cluster’s wildcard DNS already resolves them.

Step 1: Create the pull secret in three namespaces

Section titled “Step 1: Create the pull secret in three namespaces”

Three things pull images, in three namespaces, and a Secret does not cross a namespace boundary:

Namespace What pulls Without the Secret
openshift-marketplace The catalog The catalog never reaches READY, and no tile appears
openshift-operators The operator The operator stays at Installing
olympus The platform The operator installs; every platform pod fails its pull
for ns in openshift-marketplace openshift-operators olympus; do
oc create namespace "$ns" 2>/dev/null || true
oc -n "$ns" create secret docker-registry <pull-secret> \
--docker-server=<registry-host> \
--docker-username=<registry-username> \
--docker-password=<registry-token> \
--dry-run=client -o yaml | oc apply -f -
done

create | apply replaces an older Secret of the same name. A plain oc create secret fails with AlreadyExists and leaves an old token in place.

In the web console: choose each namespace in the Project list, then Workloads › Secrets › Create › Image pull secret.

The Olympusx AI operator is distributed through its own catalog, not a public listing. The catalog image reference comes with your subscription.

Save this as olympus-catalog.yaml:

apiVersion: operators.coreos.com/v1alpha1
kind: CatalogSource
metadata:
name: olympus-operators
namespace: openshift-marketplace
spec:
sourceType: grpc
image: <catalog-image>
displayName: Olympusx AI
publisher: Olympusx.ai
secrets:
- <pull-secret>
updateStrategy:
registryPoll:
interval: 30m
---
apiVersion: operators.coreos.com/v1alpha1
kind: Subscription
metadata:
name: olympus-operator
namespace: openshift-operators
spec:
channel: stable
name: olympus-operator
source: olympus-operators
sourceNamespace: openshift-marketplace
installPlanApproval: Automatic
oc apply -f olympus-catalog.yaml

Keep the names as written. The Subscription finds the catalog by source and sourceNamespace; a sourceNamespace with no matching CatalogSource never resolves and reports no error. The operator goes in openshift-operators because it watches all namespaces.

Approval. Automatic installs at once and applies newer versions the catalog offers. Manual holds every install and upgrade, this first one included, until someone approves it; production clusters usually take Manual so an upgrade waits for its change window.

Wait for the catalog, about a minute:

oc -n openshift-marketplace get catalogsource olympus-operators \
-o jsonpath='{.status.connectionState.lastObservedState}{"\n"}'
# READY
oc get packagemanifest | grep olympus
# olympus-operator Olympusx AI 21s

The Olympusx AI tile then appears in Ecosystem › Software Catalog. The package underneath it is olympus-operator.

The operator runs as the ServiceAccount olympus-operator-controller-manager, which the install creates at the same moment as the pod that needs it. The first operator pod fails its pull; that is expected. Wait for the account, link the Secret to it, and replace the pod:

oc -n openshift-operators get serviceaccount olympus-operator-controller-manager
oc -n openshift-operators patch serviceaccount olympus-operator-controller-manager \
-p '{"imagePullSecrets":[{"name":"<pull-secret>"}]}'
oc -n openshift-operators delete pod \
-l app.kubernetes.io/name=olympus-operator,control-plane=controller-manager

The delete is not optional. A pod keeps the pull secrets it was created with, so linking the account does nothing for a pod that already exists. Do not rely on the cluster-wide pull secret for a private repository.

Wait for the operator, about a minute:

oc -n openshift-operators get csv olympus-operator.v<version> \
-o jsonpath='{.status.phase}{"\n"}'
# Succeeded

Ecosystem › Installed Operators shows Olympusx AI with the status Succeeded. A cluster that only serves models for an Olympusx AI running elsewhere stops here.

The operator refuses to install without it:

oc -n olympus create secret generic olympus-master-key \
--from-literal=SECRETS_MASTER_KEY="$(openssl rand -base64 32)"

The key name inside the Secret must be SECRETS_MASTER_KEY exactly. Store a copy now, with your other master keys:

oc -n olympus get secret olympus-master-key \
-o jsonpath='{.data.SECRETS_MASTER_KEY}' | base64 -d

Losing it loses everything it sealed, including in a restored database.

oc -n olympus create secret generic olympus-smtp \
--from-literal=password='<relay-password>'

The resource names this Secret; the password never appears in the resource.

Read the guardrails image digest the release pins:

oc -n openshift-operators get csv olympus-operator.v<version> \
-o jsonpath='{.spec.relatedImages[?(@.name=="olympus-guardrails")].image}{"\n"}'

The output ends in @sha256:.... Use the sha256:... part, without the image name, as <guardrails-digest> below.

Save this as olympus.yaml:

apiVersion: olympus.olympusx.ai/v1
kind: Olympus
metadata:
name: olympus
namespace: olympus
spec:
ingress:
consoleHost: <console-host>
gatewayHost: <gateway-host>
auth:
issuer: https://<console-host>/auth/realms/olympus
secrets:
masterKey: olympus-master-key
registry: <pull-secret>
database:
mode: managed
storageClass: <storage-class>
walStorageClass: <storage-class>
components:
keycloak: true
keycloakBootstrapUser: <admin-email>
keycloakSmtp:
host: <smtp-host>
port: 587
encryption: starttls
from: <sender-address>
auth:
username: <smtp-username>
passwordSecret: olympus-smtp
guardrails: true
guardrailsImage: '<guardrails-digest>'
Field What it does
ingress.consoleHost, ingress.gatewayHost The two Routes. Sign-in is served on the console’s host under /auth
auth.issuer With the built-in identity service it must be exactly https://<console-host>/auth/realms/olympus
secrets.masterKey, secrets.registry The Secrets from steps 4 and 1
database.mode: managed Three PostgreSQL instances in this namespace, one primary
database.storageClass, walStorageClass Where the data and write-ahead-log volumes come from
The identity service line under components Deploys the built-in identity service, which signs people in
components.keycloakBootstrapUser The first platform administrator. Created once, at first start
components.keycloakSmtp The relay invitations go through. For port 465 set encryption: ssl; for a relay without login, leave out auth
components.guardrails, guardrailsImage The guardrails service, pinned by digest. A bare digest, never a full image reference
oc apply -f olympus.yaml

In the web console: Ecosystem › Installed Operators, project olympus, Olympusx AI, the Olympusx Platform tab, Create Olympusx AI, YAML view. Replace the example with your YAML, then Create.

A missing required field is refused at once, by name, for example spec.auth.issuer: Required value. Every other field, including backups and tenant isolation, is in Configuration and can be added to the running resource later with oc -n olympus edit olympuses.olympus.olympusx.ai olympus.

Guardrails start in observe mode. From the first request, every prompt and answer passes through the scanner, and every tenant starts in Observe: findings go to the audit trail and nothing is blocked or changed. A tenant administrator switches to enforcement in Guardrails. While the scanner is not ready, gateway requests are refused with guardrails_unavailable, in either mode.

oc -n olympus get olympuses.olympus.olympusx.ai olympus -w
# NAME VERSION COMPONENTS READY AGE
# olympus 1.1.0 3/11 Unknown 20s
# olympus 1.1.0 10/11 Unknown 4m
# olympus 1.1.0 11/11 True 18m
  • COMPONENTS counts what serves: nine Deployments (the control plane, the gateway, the console, the metering and budget workers, the cache, the document converter, the identity service and the guardrails service), the object store and the database. Without the identity and guardrails services it ends at 9/9.
  • Unknown means still installing, not an error. False means the operator refused, and the reason names why (Troubleshooting).
  • Allow 15 to 30 minutes on a first install. The guardrails image is about 4.5 GB to download on each of the two nodes that run it.

Three warnings are expected on this resource, and none of them stops the install: no backup destination, tenant isolation off, and the model serving provider chosen for OpenShift.

oc -n olympus get events --field-selector reason=InstallWarning
oc -n olympus get route

The console answers on <console-host>, the gateway on <gateway-host>. Continue with First sign-in, then Verify the installation.

The operator asks the cluster whether it serves Routes and, when it does:

Effect
Routes, not Ingress Renders two Routes with edge TLS. ingress.className and ingress.tlsSecret are reported as ignored
No pinned user IDs Removes the fixed user and group IDs that restricted-v2 rejects. No SecurityContextConstraints object of your own is needed: the pods run without root, privilege escalation or capabilities, on a read-only root filesystem
Model serving Each model runs as its own Deployment and Service on a registered cluster
NetworkPolicy peers Admits OpenShift’s monitoring namespaces to the metrics ports, and openshift-operators to the database’s management port

Monitoring (components.monitoring) needs user-workload monitoring turned on by a cluster administrator.

On OpenShift 4.14 to 4.19, or when you need a chart value the resource does not expose, install with the Helm chart and its OpenShift values. Do not run both paths against one namespace.

  1. Create the project, the pull secret and the Secrets as in Install on Kubernetes, with oc in place of kubectl:

    oc new-project olympus
  2. Write olympus-values.yaml as on that page, without the ingress block, and with the console host for the Route:

    route:
    consoleHost: <console-host> # required
    gatewayHost: <gateway-host> # optional: OpenShift generates one when empty

    route.consoleHost is required, and the chart refuses to render without it: every token’s issuer is built from it.

  3. Pull the chart, which carries values-openshift.yaml, and install with that file first and yours second:

    helm pull oci://<registry>/charts/olympus --version <version> --untar -d ./chart
    helm install olympus ./chart/olympus -n olympus \
    -f ./chart/olympus/values-openshift.yaml \
    -f olympus-values.yaml --wait --timeout 20m

values-openshift.yaml turns Routes on and Ingress off, removes the pinned user IDs, selects the managed database with three instances, turns monitoring on, and leaves the backup destination empty on purpose: the install stops with a message naming postgresqlCNPG.backup.destinationPath until you set it. For a first smoke test with no point-in-time recovery, add --set postgresqlCNPG.backup.enabled=false so the choice is on record in your shell history.

Keep the same file order on every upgrade. Reversed, an upgrade reverts to Ingress and pinned user IDs.