Skip to main content

Deploy DALP to OpenShift

Apply the OpenShift values overlay to run the DALP chart under a restricted security context and Routes instead of ingress.

To run DALP on OpenShift, install the same dalp chart with its values-openshift.yaml overlay layered over your values. The overlay runs every pod under the restricted-v2 Security Context Constraint (SCC), the OpenShift admission policy that governs a pod's identity and capabilities, and serves traffic through OpenShift Routes instead of ingress or Gateway API, so the Gateway API CRD bundle is not needed.

Apply the values overlay

The overlay ships inside the chart package. Pull and unpack the chart, then apply the overlay as a second values file over your own, in this order:

helm pull oci://pkg.distribution.settlemint.com/settlemint/dalp --version <chart version> --untar
helm install dalp ./dalp \
  -f values.yaml \
  -f ./dalp/values-openshift.yaml

The overlay ships local test hostnames under apps-crc.testing. Before you install anywhere but a local CodeReady Containers cluster, set every openShiftRoute.host, global.dalpApp.config.app.http.url and the https://dapi.apps-crc.testing entry in global.dalpApp.config.auth.consoleUrls, as Preconditions beyond the security context describes with the other prerequisites.

The observability chart installs as its own release, so it ships its own values-openshift.yaml beside its values.yaml, layered the same way:

helm install observability ./observability -f values.yaml -f ./observability/values-openshift.yaml

A dalp release never reads another chart's values, so these overrides take effect only in the release of the chart that owns them. Preview the output first: run helm template with the same flags in place of install.

The overlay declares empty runAsUser, runAsGroup and fsGroup fields globally, so no chart introduces a fixed identity. It sets runAsNonRoot: true and seccompProfile: RuntimeDefault at both pod and container level, and allowPrivilegeEscalation: false with all capabilities dropped at container level. Four subcharts ship a fixed identity of their own: the block explorer's frontend (1001), PgBouncer (70), the PostgreSQL exporter (65534) and the simulator (65532). The overlay clears those fields in each chart's own block.

That global block sits under global.securityContexts and reaches most charts in the release through one shared merge helper: the Console and the block explorer inherit the cleared identity fields, the non-root requirement and the dropped Linux capabilities from it. Other workloads have separate security-context blocks in the overlay because their subcharts, sidecars or Jobs cannot use the shared helper. These include the block explorer's frontend, PgBouncer, its metrics sidecar, the Investor portal, the Platform API and the PostgreSQL exporter. Grafana, kube-state-metrics, VictoriaLogs and Tempo have theirs in the observability overlay. The Platform API's deployment templates read only dapi.podSecurityContext and dapi.containerSecurityContext, so the block the overlay sets under dapi: is its only source of security context. The overlay mounts no application container's root filesystem read-only. It sets readOnlyRootFilesystem: true only on the PgBouncer metrics sidecar and the PostgreSQL exporter.

Routing

The overlay disables ingress and serves through native Routes. Every workload that serves HTTP has its own openShiftRoute block that sets the host, labels and TLS, and not every one ships turned on. The 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. The Console reaches the Platform API inside the cluster, and most integrators use the Console's API path. Set dapi.openShiftRoute.enabled to true only when integrators must call the Platform API directly. Gateway API httpRoute is disabled for the Platform API on OpenShift, and Routes replace it. The Console Route sets an HAProxy router timeout of 5 minutes, above the worst-case create and write latency, so a slow request completes instead of returning a 504.

The request path from a browser to a Console pod under the OpenShift overlay.A browser sends an HTTPS request to the Route host. The Route terminates TLS at the router and redirects any plain HTTP request to HTTPS instead of serving it. It then forwards the request into the cluster as plain HTTP, first to the Service, a ClusterIP with no external exposure of its own, then to a pod. That pod runs under the restricted-v2 security context: non-root, no added Linux capabilities, and a user and group ID that OpenShift assigns rather than the chart.The request path from a browser to a Console pod under the OpenShift overlay.A browser sends an HTTPS request to the Route host. The Route terminates TLS at the router and redirects any plain HTTP request to HTTPS instead of serving it. It then forwards the request into the cluster as plain HTTP, first to the Service, a ClusterIP with no external exposure of its own, then to a pod. That pod runs under the restricted-v2 security context: non-root, no added Linux capabilities, and a user and group ID that OpenShift assigns rather than the chart.

A browser's HTTPS request ends at the Route, continues as plain HTTP through the Service, and reaches a pod running under restricted-v2.

Every Route in the overlay terminates TLS at the router with termination: edge and insecureEdgeTerminationPolicy: Redirect: HTTPS ends at the OpenShift router, traffic continues unencrypted inside the cluster over the Service network, and a plain HTTP request is redirected to HTTPS. The chart does not restrict this value. The Route template passes your tls block through unchanged, so passthrough, where the original TLS session reaches the pod, or reencrypt, where the router terminates TLS and encrypts again 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, sets the lowest CPU and memory each container may request and rejects any request below it. The chart's own defaults fall under a common floor of 100m CPU and 128Mi memory in several places: the Conjur authenticator init container ships 10m CPU and 32Mi memory. The overlay raises the containers in the table to at least that floor. The Conjur and Console blocks in the example show the shape of the overlay's resources keys, and the other containers follow the same pattern. These are the overlay's values, and they replace the base chart's sizing in Resource sizing.

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 /api/v2 fleet
600m / 1536Mi
1500m / 3Gi
Platform API /api/v1 fleet
300m / 768Mi
1000m / 1536Mi
Runner
250m / 1536Mi
1500m / 3Gi
Indexer
1250m / 1536Mi
2000m / 3Gi
contracts-deploy Job
250m / 1Gi
2 CPU / 4Gi
PostgreSQL exporter (disabled unless you turn it on)
100m / 128Mi
200m / 128Mi

Compare your namespace's LimitRange floor with every container in the table before you deploy. 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, serve the chart and every image from a registry inside your network, in addition to the SCC and LimitRange settings. List the images a release uses with helm template dalp ./dalp -f values.yaml -f ./dalp/values-openshift.yaml | grep 'image:' | sort -u, and mirror each one by digest. Confirm every image reference, init and utility images included, carries an explicit registry and repository plus a tag or digest, because an air-gapped cluster cannot resolve an implicit public registry. The overlay supplies mirrored-registry overrides for the kubectl helper and the Conjur authenticator. A correct registry override cannot load an image whose digest is missing from the mirror.

The Platform API, the Workflow Engine, Ledger Data and the scheduler run as separate images, each with its own repository value and each distroless with no shell, so a mirrored registry needs all four. Any entrypoint wrapping these images, including the Conjur summon injection described in Values and secrets, must run in exec form, not through /bin/sh -c.

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 unchanged renders Routes on hostnames no production cluster serves. 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.

Set the Console public URL only in global.dalpApp.config.app.http.url. The Console and the Platform API both read that one value: the 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 observability overlay pins the storage classes of VictoriaMetrics and VictoriaLogs to one name, standard-csi. Without a StorageClass named standard-csi, those PersistentVolumeClaims stay Pending, and their provisioning events name the missing class. Set server.persistentVolume.storageClassName for VictoriaMetrics and VictoriaLogs to a class your cluster provides.

The overlay also turns on deny-by-default egress for two workloads: dapp.networkPolicy.enabled and investorPortal.networkPolicy.enabled both become true. The Console policy declares both Ingress and Egress policy types, and its egress allow list selects pods only, which covers same-namespace, chart-deployed dependencies. It does not reach an object store, an SMTP relay or a database outside the cluster. Before you apply the overlay, add ipBlock peers for those external dependencies to dapp.networkPolicy.egress, and to investorPortal.networkPolicy.egress when the portal is enabled. An endpoint missing from the allow list cannot be reached, and the blocked connection produces no admission error.

Browser apps with user sessions

When the Console and the Investor portal use separate Routes, each app serves /api and /dalp on its own host and forwards to the private DAPI_INTERNAL_URL. 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. Do not configure a public DAPI_URL for the Portal. Deploy browser apps with user sessions lists the values, and the SDK browser guide shows the proxy handler.

Troubleshooting

Where in the pod lifecycle an OpenShift deployment failure lands.If the deploy fails or a pod stays unhealthy, start with when the failure happens rather than how it looks. A rejection before the pod object exists is an SCC error, the only case with no pod yet to inspect. A pod that exists but never starts is stuck on a LimitRange floor, a missing StorageClass, or a failed image pull, and the event attached to the pod names which. A pod that is already running and then fails points to the Route, the NetworkPolicy egress allow list, or the router timeout, none of which leave any event on the pod at all. Each case is covered in the accordions below.Where in the pod lifecycle an OpenShift deployment failure lands.If the deploy fails or a pod stays unhealthy, start with when the failure happens rather than how it looks. A rejection before the pod object exists is an SCC error, the only case with no pod yet to inspect. A pod that exists but never starts is stuck on a LimitRange floor, a missing StorageClass, or a failed image pull, and the event attached to the pod names which. A pod that is already running and then fails points to the Route, the NetworkPolicy egress allow list, or the router timeout, none of which leave any event on the pod at all. Each case is covered in the accordions below.

A deployment failure lands in one of three phases: rejected before the pod exists, stuck before it starts, or failing once it is running.

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 roles, contracts-deploy Job and PostgreSQL exporter. A request below the floor is rejected at admission and 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 observability overlay pins for VictoriaMetrics and VictoriaLogs. A cluster without that class leaves the PVC Pending with a provisioning event naming the missing class. Point server.persistentVolume.storageClassName at a class your cluster 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 a missing Route for them is the shipped state. 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 allow list covers only same-namespace, chart-deployed dependencies. Anything else needs an explicit ipBlock peer, or its connections fail with no admission error, which looks like a network outage.

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 from the router before the application finishes. Raise openShiftRoute.timeout for the affected service.

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 ./dalp/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?

The overlay is written for restricted-v2, and the chart does not enforce it. The overlay clears fixed identities, drops capabilities and runs as non-root, which a less restrictive SCC also admits. 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 leaves the Service each chart creates in place, so traffic inside the cluster keeps working as it does off OpenShift.

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.

Was this page helpful?