Red Hat OpenShift
Read this page before step 1 to install OpenLM Platform on Red Hat OpenShift. OpenShift is Kubernetes with stricter defaults, and OpenLM Platform runs on it without special privileges: no anyuid, no privileged containers, and no cluster-admin for the standard component set. This page explains what's different, what the chart does about it, and the few extra steps you take.
What's different on OpenShift, and how OpenLM Platform handles it
The following table shows what OpenShift does differently and how the OpenLM chart handles it:
| OpenShift does… | Effect on an ordinary chart | What the OpenLM chart does |
|---|---|---|
Runs every pod under the restricted-v2 SCC: a random user ID from the project's range, no root, no privilege escalation, all capabilities dropped | Pods with a fixed user ID (for example 1000) are rejected | With global.useOpenShift: true, the chart leaves the user and group IDs out, so OpenShift assigns them. Every pod still declares runAsNonRoot, seccompProfile: RuntimeDefault, allowPrivilegeEscalation: false and drops all capabilities, which is what restricted-v2 requires. A few components need their IDs set explicitly in this release (see Values). |
| Brings traffic in with Routes and its own router | Ingress objects with controller-specific annotations don't behave as intended | With useOpenShift: true, the chart creates Routes: edge TLS at the router, HTTP-to-HTTPS redirect, HSTS, and security headers. |
| Often gives you a project, not the cluster | You can't label nodes, install CRDs, or create cluster roles | The chart needs none of that. Leave node placement off. |
| Often pulls images through a mirror | Image names must be rewritten or mirrored | OpenLM images use one registry path, public.ecr.aws/r3q3q2f4, and the API gateway also pulls from docker.tyk.io. Mirror both (see Images). |
| Applies LimitRanges and ResourceQuotas per project | Pods that request less than the project's minimum are rejected, and big installations hit the quota | Plan the quota, and ask for the minimums to be removed: see Resources and quotas. |
OpenLM Platform images run as an arbitrary user ID with group 0, the OpenShift convention, with one exception in this release: denialsaggregation, the scheduled job that prepares the denials reports. Under an arbitrary user ID, each of its runs ends with Error. The rest of OpenLM Platform isn't affected, but the denials reports aren't updated.
Before you start
Confirm these requirements:
| Item | Requirement |
|---|---|
| OpenShift | 4.18 or newer, including Red Hat OpenShift Service on AWS (ROSA) and Azure Red Hat OpenShift (ARO) |
| Your rights | admin on one project. No cluster-admin needed for the standard set. |
| Tested on | OpenShift 4.18 (ROSA) and the OpenShift 4.20 family (Kubernetes 1.33) in production |
| Cluster-level objects | None for the standard set. The optional admission webhook component creates cluster-level objects and needs a cluster admin. It isn't part of the standard set. |
| Storage | A block storage class: gp3-csi on ROSA, managed-csi on ARO, your CSI block class elsewhere (for example thin-csi, ocs-storagecluster-ceph-rbd). Many clusters default to a file-based class (NFS or SMB). Don't use it for the platform's volumes or for databases and Kafka: set STORAGE_CLASS to the block class explicitly. |
| DNS name | Either a name under the cluster's apps domain (openlm.apps.<cluster-domain>, simplest), or your own name with a certificate. See Certificates. |
In the worksheet, set ON_OPENSHIFT="true". Leave INGRESS_CLASS at any value: OpenShift doesn't use it.
Create the project
Create the project and read its user ID range:
oc new-project openlm
oc get project openlm -o jsonpath='{.metadata.annotations.openshift\.io/sa\.scc\.uid-range}{"\n"}'
The second command prints the project's user ID range, for example 1002360000/10000. The script in Values uses it.
Values
After step 4, generate a second values file for OpenShift. The commands load their variables from openlm-install.env, the file you created from the installation worksheet. You pass both files to Helm. Helm merges them, so nothing in the first file needs to change.
source ./openlm-install.env
UID_START="$(oc get project "$NS" -o jsonpath='{.metadata.annotations.openshift\.io/sa\.scc\.uid-range}' | cut -d/ -f1)"
F=openlm-values-openshift.yaml
cat > $F <<EOF
# OpenShift additions for $DOMAIN (project UID range starts at $UID_START)
global:
useOpenShift: true # Routes instead of Ingress; OpenShift assigns user IDs
masterIngress: { enabled: false }
EOF
# Every Route carries the host name
for c in agentactivitymanagerui agentshubui alertsserviceui auditserviceui brokerhubui cloudbrokerui \
complianceserviceui dbconfigurationtoolui denialsui donglemonitoringui dssui homepageui identity \
identityui integrationui licenseaccesscontrolui licenseallocationsui licensefilesui licensemanagerui \
licenseserversui licensesui navigationui notificationui personaldashboard processmanagerui \
productsserviceui projectsserviceui samcatalogui \
softwareassetmanagementui uiconfigurationserviceui usageui usersandgroupsserviceui vlmui; do
printf '%s:\n ingress: { hostname: %s }\n' "$c" "$DOMAIN" >> $F
done
# Components that set their own user IDs: give them IDs from the project's range
for c in licensefilesmanagement platformhealth; do
printf '%s:\n securityContext:\n pod: { runAsUser: %s, runAsGroup: 0, fsGroup: %s, runAsNonRoot: true, seccompProfile: { type: RuntimeDefault } }\n' \
"$c" "$UID_START" "$UID_START" >> $F
done
echo "written: $F"
Then install with both files. This replaces the command in step 6:
helm upgrade --install openlm-platform openlm/openlm-platform --version "$OPENLM_VERSION" -n "$NS" \
-f openlm-values.yaml -f openlm-values-openshift.yaml --timeout 60m
Routes
The chart creates a Route for each web UI and for sign-in. Never change a Route's host on upgrade: it stays your DOMAIN. In this release, take three extra steps after every installation and upgrade.
1. If you switched on data anonymization, create its Route, which the chart doesn't create yet. Without it, that path shows the home page instead. The oc delete route lines apply to every installation. (The /api Route comes from the gateway release in step 5.)
oc -n "$NS" create route edge openlm-anonymization --service=openlm-anonymizationui --hostname="$DOMAIN" \
--path=/anonymization --insecure-policy=Redirect --dry-run=client -o yaml | oc -n "$NS" apply -f -
oc -n "$NS" annotate route openlm-anonymization --overwrite haproxy.router.openshift.io/rewrite-target=/
# Only if an earlier installation created these by hand: remove them
oc -n "$NS" delete route openlm-api openlm-dbconfigurationtool openlm-identity-alignment \
openlm-process-sessions openlm-touch-point-events --ignore-not-found
If a Route you created by hand has the same name as one the chart now creates, the upgrade stops before it changes anything. Either delete the hand-made Route with the command in step 1, or let Helm take it over: add the label app.kubernetes.io/managed-by=Helm and the annotations meta.helm.sh/release-name=openlm-platform and meta.helm.sh/release-namespace=openlm to it, then run the upgrade again.
2. Let sign-in keep its path. The sign-in Route must pass /identity through unchanged. Without this, sign-in fails with 409:
oc -n "$NS" annotate route identity-ingress haproxy.router.openshift.io/rewrite-target-
3. Check the Routes:
oc -n "$NS" get routes -o custom-columns=NAME:.metadata.name,HOST:.spec.host,PATH:.spec.path | sort -k3
Every Route shows your DOMAIN, and the paths include /, /api and /identity.
Certificates
Routes terminate TLS at the OpenShift router. The chart's Routes don't carry a certificate of their own, so the router serves its default certificate. The following table shows the three cases:
Your DOMAIN | Router certificate | What to do |
|---|---|---|
Under the apps domain (openlm.apps.<cluster>), and the router's certificate is from a public CA, the usual case on ROSA | Trusted | Nothing |
| Under the apps domain, router certificate from the cluster's own CA (the default on a new self-managed cluster) | Not trusted by pods | Ask the cluster admin to install a trusted default certificate on the router, or use a name with its own certificate |
Your own name (openlm.example.com) | Doesn't cover it | Ask the cluster admin to serve a certificate for your name on the router (default certificate or a router shard), or add the certificate to each Route (spec.tls.certificate / key), which needs the routes/custom-host permission |
To check the certificate the pods see, run this command:
echo | openssl s_client -connect "$DOMAIN:443" -servername "$DOMAIN" 2>/dev/null | openssl x509 -noout -subject -issuer -enddate
The subject or alternative names must cover DOMAIN, and the issuer must be a CA your pods trust. TLS certificates explains why this matters for sign-in.
You still create openlm-lb-cert in step 3. OpenShift doesn't use it, but keeping it makes the secret set identical on every platform.
Images
If the cluster can't pull from public.ecr.aws, mirror public.ecr.aws/r3q3q2f4 into your registry and add an ImageTagMirrorSet (cluster admin), or set up a pull-through remote repository (Quay, Artifactory, Nexus). Image tags stay the same.
Pull-through registries often scan each new image on its first pull and hold it until the scan finishes, and public registries limit anonymous pulls. Pull the release's images through your registry a day or more before the installation or upgrade window. The image list is on the release's release notes page. Give the remote repository credentials for the public registry.
An upgrade pulls dozens of new images at once. Check that the workers have free image storage before you upgrade. A worker that runs low on disk evicts pods (DiskPressure).
Resources and quotas
Plan the project quota with these figures:
- The standard set requests about 10 vCPU / 22 GiB and has limits of about 55 GiB of memory in total. Projects often have a quota on limits, not requests. It must allow the limits, plus the API gateway, plus room for an upgrade, when old and new pods run side by side. Ask for
limits.cpu: 64,limits.memory: 128Gi. The Sizing page has the details. - A LimitRange minimum blocks most of the platform: most components request little (the smallest 5m CPU and 10 MiB of memory), and OpenShift rejects a pod whose request is below the minimum, with an event such as
minimum cpu usage per Pod is 100m. Ask the cluster admin to remove the project's minimums, or to set them no higher than 5m CPU and 10 MiB. Defaults and maximums are fine. - OpenLM Platform uses 4 PersistentVolumeClaims, and the API gateway 3 more. A project with a PVC quota needs room for at least 7, plus the volumes of any data services you run in the project.
Data services on OpenShift
Run MongoDB, Kafka and Redis outside the project (managed services or your platform team's), or with operators made for OpenShift (for example Strimzi or AMQ Streams for Kafka). restricted-v2 rejects many generic charts, because they hard-code user IDs.
If Kafka runs in the project, set a size limit (retention.bytes) on every topic so retention can't fill its volume. Size the volume as described in Sizing.
Backups
CSI volume snapshots work for the platform's volumes: a VolumeSnapshot of each PVC in the project. Back up SQL Server, MongoDB and Kafka with their own tools.
Checks specific to OpenShift
Run these commands. Each prints nothing on a healthy installation:
oc -n openlm get pods --no-headers | grep -vE "Running|Completed" # nothing
oc -n openlm get events --field-selector reason=FailedCreate | grep -i "unable to validate against any security context constraint" # nothing
In this release, the first command also lists failed openlm-denialsaggregation-… job pods. They are expected, as What's different on OpenShift explains. Any other pod in its output needs attention.
A FailedCreate event that mentions the security context constraints names the component that still sets a fixed user ID. Add it to the second loop in Values and run the installation again.
The documentation team might draft this page using a large language model. A technical writer reviews every page and verifies it against the product before publishing it. A human makes the final decision.