Deploy DALP to OpenShift
Apply the OpenShift values overlay to run the dalp chart under a restricted security context and Routes instead of ingress.
Overlay, not a separate chart
OpenShift is a supported target for the same dalp chart templates. Apply values-openshift.yaml as a second values file over the base, in this order:
helm install dalp ./dalp \
-f values.yaml \
-f values-openshift.yamlThe support and observability charts install as their own releases, so each ships its own values-openshift.yaml beside its values.yaml, layered the same way:
helm install support ./support -f values.yaml -f values-openshift.yaml
helm install observability ./observability -f values.yaml -f values-openshift.yamlA dalp release never reads another chart's values, so these overrides only take effect in the release of the chart that owns them.
Render the manifests first with helm template (the same flags, in place of install) to preview the output before you commit to a release.
The overlay declares empty runAsUser, runAsGroup, and fsGroup fields globally, so no chart introduces a fixed identity, and sets runAsNonRoot: true, allowPrivilegeEscalation: false, and seccompProfile: RuntimeDefault explicitly at both pod and container level. The only fixed identity anywhere in the chart tree belongs to the block explorer's frontend (runAsUser: 1001, runAsGroup: 1001, fsGroup: 1001), and the overlay clears those three fields in that chart's own block. The reference target is the restricted-v2 security context constraint (Security Context Constraint, the OpenShift admission policy that governs a pod's allowed identity and capabilities).
That global block sits under global.securityContexts and reaches most charts in the release through one shared merge helper: Console and the block explorer inherit the cleared identity fields, the non-root requirement, and the dropped Linux capabilities from this one place. Twelve workloads have separate security-context blocks in the overlay because their subcharts, sidecars, or Jobs cannot consume the shared helper in the same way. These include the block explorer's frontend, PgBouncer, its metrics sidecar, the Investor portal, the Platform API, and the PostgreSQL exporter. RustFS and the reloader operator have their blocks in the support overlay; Grafana, kube-state-metrics, VictoriaLogs, and Tempo have theirs in the observability overlay. The Platform API is one of them: its deployment template reads only dapi.podSecurityContext and dapi.containerSecurityContext, so the block the overlay sets under dapi: is the sole source of its security context, not a restatement of a global default it also inherits. The overlay does not mount any application container's root filesystem read-only: the two places it sets readOnlyRootFilesystem: true are the PgBouncer connection pool's own metrics sidecar and the standalone PostgreSQL metrics exporter, which stays disabled unless you turn it on. Neither is Console or the Platform API.
Routing
Ingress is disabled on OpenShift in favor of native Routes: every workload that serves HTTP gets its own openShiftRoute block setting the host plus labels and TLS, but not every one ships turned on. Console, the block explorer's frontend, and the docs site enable openShiftRoute by default. The block explorer's API and the Platform API disable it by default. Console reaches the Platform API inside the cluster, and most integrators use Console's API path. Flip dapi.openShiftRoute.enabled to true only when integrators need to call the Platform API directly rather than through the Console. Gateway API httpRoute is disabled outright for the Platform API on OpenShift; Routes replace it rather than sit alongside it. The Console Route sets an HAProxy router timeout of 5 minutes, sized above its worst-case create and write latency, to avoid returning a 504 on a slow request instead of letting it complete.
The request path under the overlay: a browser's HTTPS request ends at the Route, continues in cluster as plain HTTP through the Service, and reaches a pod running under the restricted-v2 security context.
Every Route in the overlay terminates TLS at the router with termination: edge and insecureEdgeTerminationPolicy: Redirect: HTTPS ends at the OpenShift router, traffic continues in cluster over the Service network, and a plain HTTP request is redirected to HTTPS rather than served. That edge setting is the value the overlay's own Routes carry, not a limit the chart imposes. The Route template passes your tls block through unchanged, so switching termination to passthrough (the original TLS session reaches the pod untouched) or reencrypt (the router terminates and then re-encrypts to the pod) needs no template edit. Passthrough requires TLS at the pod. The Console service port uses plain HTTP, so changing the Route value alone cannot enable passthrough. Reencrypt also needs a destinationCACertificate and a pod that serves TLS on the port the Route re-encrypts to.
LimitRange floor
A namespace LimitRange, a separate admission check from the restricted-v2 SCC, floors the CPU and memory each container may request and rejects any request below it. The chart's own defaults undershoot a typical floor in several places: the Conjur authenticator init container ships 10m CPU and 32Mi memory, well under a common 100m CPU / 128Mi memory minimum. The overlay raises every container a typical LimitRange catches to at or above that floor, not only Conjur and Console. The Conjur and Console blocks below show the shape the overlay's resources keys take; the rest follow the same pattern in their own resources keys, listed in the table after them.
global:
conjur:
resources:
requests:
cpu: "100m"
memory: "128Mi"
limits:
cpu: "200m"
memory: "256Mi"
dapp:
resources:
requests:
cpu: 100m
memory: 256Mi
limits:
cpu: "1"
memory: 512Mi| Container | CPU / memory request | CPU / memory limit |
|---|---|---|
| Conjur authenticator init container | 100m / 128Mi | 200m / 256Mi |
| Console | 100m / 256Mi | 1 CPU / 512Mi |
| Console migrator Job | 100m / 256Mi | 500m / 512Mi |
Console tcpCheck init container | 100m / 128Mi | 300m / 128Mi |
| PgBouncer metrics sidecar | 100m / 128Mi | 200m / 128Mi |
| Platform API | 250m / 1536Mi | 1500m / 3Gi |
| Redis | 250m / 768Mi | 1 CPU / 1Gi |
| contracts-deploy Job | 250m / 1Gi | 2 CPU / 4Gi |
| PostgreSQL exporter (disabled unless you turn it on) | 100m / 128Mi | 200m / 128Mi |
Confirm your namespace's actual LimitRange floor against every row in this table before you deploy, not only Conjur and Console. A floor set higher than the overlay's requests rejects the pod at admission, and the workload controller reports a failed pod creation with an admission event naming the container and invalid request. A floor set lower still admits the pod but wastes the headroom the overlay already budgeted. For how the Conjur authenticator init container receives its credentials, see Values and secrets.
Preconditions beyond the security context
For an air-gapped OpenShift deployment, configure a mirrored image registry in addition to the SCC and LimitRange settings. Confirm every image reference (including init and utility images) carries an explicit registry and repository, plus a tag or digest, since an air-gapped cluster cannot resolve an implicit public registry. The overlay supplies mirrored-registry overrides for the kubectl helper and Conjur authenticator. Confirm that your mirror contains the exact pinned digests. A correct registry override cannot load an image whose digest is missing.
Every Route host, the Console public URL, and the direct Platform API Route origin ship as placeholder apps-crc.testing hostnames, the domain a local CodeReady Containers cluster serves. Applying the overlay exactly as shown renders Routes on hostnames no production cluster serves. Before you install anywhere but a local CodeReady Containers cluster, override every openShiftRoute.host, global.dalpApp.config.app.http.url, and the https://dapi.apps-crc.testing entry in global.dalpApp.config.auth.consoleUrls. When you enable the Investor portal, also set global.dalpApp.env.INVESTOR_PORTAL_URL to its public Route host: the overlay ships no value for it, and the render fails without one. The Investor portal needs no platform URL of its own: its server forwards the browser's /api and /dalp calls to DAPI_INTERNAL_URL in investorPortal.env, which defaults to the in-cluster http://dalp-api:3001.
Browser apps with user sessions
When the Console and Investor portal use separate Routes, each app serves /api and /dalp on its own host and forwards to the private DAPI_INTERNAL_URL. Do not configure a public DAPI_URL for the Portal. See same-origin browser app deployment for the values and the SDK browser guide for the proxy handler.
Set the Console public URL only in global.dalpApp.config.app.http.url. Console and the Platform API both read that one value: Console sends it as the origin of its server-side calls, and the Platform API accepts a Console session only from the origins it lists. Give it as a plain https:// URL. The chart refuses to render dapp.config.app or dapi.config.app, a ${...} variable in the URL, or a Helm template expression anywhere in global.dalpApp.config, because a second copy of the URL, or one that resolves differently per component, lets the two disagree and signs every user out on a full page load.
The support and observability overlays pin four storage classes to a single name, standard-csi: Redis persistence, PostgreSQL persistence, VictoriaMetrics, and VictoriaLogs. Without a StorageClass named standard-csi, those PersistentVolumeClaims remain Pending. Their provisioning events name the missing class. Inspect those events separately from a LimitRange rejection, which prevents pod creation. Set persistence.storageClass for Redis and PostgreSQL, and server.persistentVolume.storageClassName for VictoriaMetrics and VictoriaLogs, to a class your cluster actually provides.
The overlay also turns on deny-by-default egress for two workloads: dapp.networkPolicy.enabled and investorPortal.networkPolicy.enabled both flip to true. The Console policy always declares both Ingress and Egress policy types, so enabling it makes egress deny-by-default too, and its egress allowlist is pod-selector only, covering same-namespace, chart-deployed dependencies. It does not know about an object store, an SMTP relay, or a database running outside the cluster. Before applying the overlay, add ipBlock peers for external dependencies to dapp.networkPolicy.egress. Update investorPortal.networkPolicy.egress too if the portal is enabled. An endpoint absent from the allowlist cannot be reached, and the blocked connection produces no admission error.
The Platform API image is distroless and carries no shell. The Workflow Engine and Ledger Data run as separate images of their own, each with its own repository value and each equally distroless, so this applies to all three and a mirrored registry needs all three. Any entrypoint wrapping the image, including the Conjur summon injection described in Values and secrets, must run in exec form rather than through /bin/sh -c.
Troubleshooting
Three phases a deployment failure can land in: rejected before the pod exists, stuck before it starts on a floor, a missing class, or a failed pull, or failing only once it is already running and serving.
Pod rejected with an SCC or security-context error
Check for a non-zero runAsGroup, runAsUser, or fsGroup still set somewhere in your values: the overlay clears
all three so OpenShift can assign compliant values itself, and a leftover fixed value anywhere in the chain overrides
that clearing. Confirm with oc get events: an SCC denial names the constraint it failed against, whether that is
runAsGroup, an added capability, or a missing seccompProfile.
Pod stuck pending on resource requests
Confirm your namespace's LimitRange floor accepts the chart's requested CPU and memory. The overlay sets minimum
requests of 100m CPU / 256Mi memory for Console and 100m CPU / 128Mi memory for Conjur. It also supplies minimum
requests for the migrator Job, tcpCheck init container, PgBouncer metrics sidecar, Platform API, contracts-deploy
Job, and PostgreSQL exporter, and the support overlay does the same for Redis. A request below the floor is rejected
at admission, not retried, so inspect the workload controller's events for rejected pod creation as well as any
existing pod's scheduling events.
PersistentVolumeClaim stuck pending
Confirm your cluster provides a StorageClass named standard-csi, the name the support and observability overlays
pin for Redis persistence, PostgreSQL persistence, VictoriaMetrics, and VictoriaLogs. A cluster without that class
leaves the PVC Pending with a provisioning event naming the missing class, not a resource or admission failure.
Point persistence.storageClass (Redis, PostgreSQL) or server.persistentVolume.storageClassName (VictoriaMetrics,
VictoriaLogs) at a class your cluster actually has.
Route not reachable
Confirm the service's openShiftRoute.enabled matches what you expect: the block explorer's own API and the Platform
API ship it false by default, so no Route existing for them is the shipped state, not a misconfiguration. For a
service you do expect a Route on, confirm openShiftRoute.enabled is true and that httpRoute/ingress are not also
enabled for the same service, since only one exposure mechanism should be active at a time. Inspect every rendered
Ingress, HTTPRoute, and Route when several exposure mechanisms are enabled. Confirm which controller owns each
hostname and its TLS settings.
Console lost an external dependency after applying the overlay
Confirm dapp.networkPolicy.egress (and investorPortal.networkPolicy.egress if the portal is on) names every external
endpoint the deployment depends on: object storage, an SMTP relay, an external database. Enabling the Console
NetworkPolicy makes egress deny-by-default, and the chart's own allowlist covers only same-namespace, chart-deployed
dependencies. Anything else needs an explicit ipBlock peer or it fails closed with no admission error to point at,
which looks like a network outage rather than a policy gap.
Console times out on a slow request instead of completing
The Console Route's HAProxy timeout is 5 minutes by default. A request that legitimately needs longer, such as a large
asset-creation write under load, returns a 504 at the router before the application finishes, which looks like an
application failure but is a router-side timeout. Raise openShiftRoute.timeout for the affected service rather than
treating the 504 as an application bug.
Image pull fails on an air-gapped cluster
An air-gapped cluster cannot resolve an implicit public registry, so any image reference without an explicit registry,
repository, and tag or digest fails to pull. Check init and utility images specifically, not only the main application
images: the kubectl utility image and the Conjur authenticator image both need their registry overridden to your
cluster's mirrored path. A missed override on either fails only the Job or init container that uses it, not the whole
release, which makes it easy to miss in a partial rollout.
Frequently asked questions
Do I need to pass both values files, or does the overlay stand on its own?
Both, in that order: -f values.yaml -f values-openshift.yaml. The overlay carries only the fields that differ under restricted-v2 and Routes; every field it does not mention, such as image tags, replica counts, and feature flags, still comes from the base file. Helm always loads the chart's built-in defaults. Pass your deployment-specific base values before the overlay so those settings are retained and the OpenShift overrides take precedence.
Is restricted-v2 mandatory, or does this overlay also work under a less restrictive SCC?
restricted-v2 is the reference target this overlay is written against, not a requirement the chart enforces. The overlay targets restricted-v2 with cleared fixed identities, dropped capabilities, and non-root execution. Verify admission against the SCC assigned in your cluster, including any local policy additions.
Can I raise the Conjur authenticator's resources further if my namespace needs more than the overlay sets?
Yes. global.conjur.resources sets one shared floor for every Conjur-enabled pod; add your own values file on top of the overlay, the same way the overlay itself raises the chart's base default, and your values take precedence. Raising it further needs nothing overlay-specific.
Does disabling ingress also disable the Kubernetes Service, cutting off traffic inside the cluster?
No. Ingress and Routes control external exposure only. Disabling ingress.enabled never touches the Service each chart creates, so Service-to-Service and Service-to-pod traffic inside the cluster keeps working exactly as it does off OpenShift; only how traffic arrives from outside the cluster changes.
Does upgrading an existing OpenShift install need anything beyond the normal upgrade path?
No overlay-specific step: run helm upgrade with the same two values files, the way you would for any other release. See Upgrades and migrations for the rollout and migration-Job mechanics that apply regardless of platform.
Observability stack
Install the observability Helm chart a DALP instance exports to: an Alloy collector, VictoriaLogs, Tempo, VictoriaMetrics, and Grafana, then verify telemetry.
SSO
Configure an external OIDC identity provider for the DALP Console, and understand that a complete entry replaces local sign-in for the whole deployment.