Skip to content

OpenShift Deployment

Audience: Administrators deploying Sentari on Red Hat OpenShift

What you need: the OpenShift bundle (download from your Sentari release), oc + helm 3.8+, a Sentari license key, and pull access to the published chart and images (or an internal mirror). No source repository required — the chart is pulled from the OCI registry.

Kubernetes users: See Kubernetes Deployment instead.


Sentari's Helm chart includes OpenShift-specific support: a Route resource (replacing Ingress) and a pod SecurityContext that omits fixed UIDs so OpenShift's default restricted-v2 Security Context Constraints (SCC) can run the workloads without any cluster-admin action.


Contents of this bundle

File Purpose
values.yaml Evaluation values — bundled TimescaleDB + Redis.
values-external-db.yaml Production values — external managed DB + Redis, NetworkPolicy + PDB on.

Which values file? Use values.yaml for a quick evaluation. Use values-external-db.yaml for production — running stateful DB/Redis images under OpenShift's restricted SCC adds UID complexity that external services avoid.


Prerequisites

  • OpenShift 4.12+ with oc logged in as a user who can create a project.
  • Helm 3.8+ (OCI support).
  • A Sentari license key.
  • Pull access to ghcr.io/sentari-dev/sentari/* (or mirror them — see Air-gap / internal registry).
  • Production: a managed PostgreSQL (TimescaleDB extension recommended) and Redis.

Step 1 — Create the project

oc new-project sentari

Step 2 — Edit your values file

Open values.yaml (evaluation) or values-external-db.yaml (production) and replace every REPLACE-... placeholder. Generate the cryptographic values:

# secretKey (64 hex)
python3 -c "import secrets; print(secrets.token_hex(32))"
# configKey (base64, 32 bytes)
python3 -c "import os,base64; print(base64.b64encode(os.urandom(32)).decode())"
# enrollmentToken / passwords
python3 -c "import secrets; print(secrets.token_hex(16))"

Set route.host to your OpenShift apps domain, e.g. sentari.apps.cluster.example.com.


Step 3 — Authenticate to the chart registry

echo "<PULL_TOKEN>" | helm registry login ghcr.io -u <your-username> --password-stdin

(Skip if you mirror the chart internally — see Air-gap / internal registry.)


Step 4 — Install

Replace <release> with the Sentari version you are deploying (e.g. 1.0.0).

Evaluation (bundled DB):

helm install sentari oci://ghcr.io/sentari-dev/sentari/charts/sentari \
  --version <release> \
  -f values.yaml \
  -n sentari

Production (external DB):

helm install sentari oci://ghcr.io/sentari-dev/sentari/charts/sentari \
  --version <release> \
  -f values-external-db.yaml \
  -n sentari

Production: keep secrets out of Helm history

Helm stores -f values in-cluster (helm get values can expose them). For production, pre-create the Secret and set secrets.existingSecretName in values-external-db.yaml:

oc create secret generic sentari-credentials -n sentari \
  --from-literal=database-url="postgresql+psycopg2://sentari:<pw>@<host>:5432/sentari" \
  --from-literal=redis-url="redis://:<pw>@<host>:6379/0" \
  --from-literal=secret-key="<64-hex>" \
  --from-literal=config-key="<base64-32-byte>" \
  --from-literal=enrollment-token="<token>" \
  --from-literal=bootstrap-admin-email="admin@example.com" \
  --from-literal=bootstrap-admin-password="<pw>" \
  --from-literal=license-key="<license>"

Then set secrets.existingSecretName: sentari-credentials and re-run the install.

Using external managed database (alternative install form)

helm install sentari oci://ghcr.io/sentari-dev/sentari/charts/sentari \
  --version 1.0.0 \
  --set openshift.enabled=true \
  --set route.host=sentari.apps.<cluster-domain> \
  --set database.external=true \
  --set redis.external=true \
  --set secrets.databaseUrl="postgresql+psycopg2://sentari:<password>@<host>:5432/sentari" \
  --set secrets.redisUrl="redis://:<password>@<host>:6379/0" \
  --set secrets.secretKey="$(python3 -c 'import secrets; print(secrets.token_hex(32))')" \
  --set secrets.enrollmentToken="$(python3 -c 'import secrets; print(secrets.token_hex(16))')" \
  --set secrets.bootstrapAdminEmail="admin@yourorganisation.com" \
  --set secrets.bootstrapAdminPassword="ChangeMe123!" \
  --namespace sentari \
  --create-namespace

The chart creates the Kubernetes Secret for you from these values — no separate step required.


Step 5 — Verify the deployment

oc get pods -n sentari -w        # wait for all pods Ready

ROUTE=$(oc get route sentari-api -n sentari -o jsonpath='{.spec.host}')
curl -sf https://$ROUTE/api/health
# Expected: {"status": "ok", "version": "..."}

Open https://$ROUTE and log in with the bootstrap admin credentials from your values file. Change the password after first login.


Step 6 — Enroll your first agent

Install the agent on an endpoint pointed at https://$ROUTE with your enrollment token — see the Agent deployment guide.


UID / Security Context Constraints

When openshift.enabled=true, the chart omits all fixed UIDs and fsGroup from the application pod SecurityContext (runAsUser, runAsGroup, and fsGroup are rendered only when openshift.enabled=false). OpenShift's default restricted-v2 SCC then assigns a UID and fsGroup from the namespace's allocated range, and the image is built to run under any injected UID.

This is the supported posture: no namespace annotation, ServiceAccount SCC annotation, or other cluster-admin action is required. A plain helm install --set openshift.enabled=true is admitted by restricted-v2 out of the box.

Bundled StatefulSets: If you deploy the bundled TimescaleDB and Redis StatefulSets, those images expect specific UIDs and may need additional SCC handling. For production OpenShift deployments, using external managed database and Redis services avoids all UID complexity and is the recommended approach.


Proxy configuration

If your OpenShift cluster routes outbound traffic through a corporate proxy, configure the proxy section in your Helm values. This is required for Sentari to reach:

  • License server (license.sentari.dev) — license validation
  • CVE feeds (osv-vulnerabilities.storage.googleapis.com) — vulnerability database sync
  • SIEM webhooks — if configured for alert delivery to an external endpoint

Helm values

proxy:
  httpProxy: "http://proxy.corp.internal:3128"
  httpsProxy: "http://proxy.corp.internal:3128"
  noProxy: "localhost,127.0.0.1,.cluster.local"

Or via --set flags:

helm install sentari oci://ghcr.io/sentari-dev/sentari/charts/sentari \
  --version 1.0.0 \
  --namespace sentari \
  --set proxy.httpProxy="http://proxy.corp.internal:3128" \
  --set proxy.httpsProxy="http://proxy.corp.internal:3128" \
  ...

The chart automatically appends the release-prefixed service names (e.g. sentari-db, sentari-redis) to NO_PROXY so that internal pod-to-pod traffic is never sent through the proxy.

If your proxy requires authentication, include credentials in the URL:

proxy:
  httpsProxy: "http://username:password@proxy.corp.internal:3128"

Verifying proxy connectivity

After deployment, verify the server can reach the license endpoint through the proxy:

oc exec deploy/sentari-api -n sentari -- \
  python3 -c "import httpx; print(httpx.get('https://license.sentari.dev/health').status_code)"

Expected: 200.


Key Helm values reference

Value Default Description
openshift.enabled false Enable OpenShift-specific resources (Route; omits fixed UIDs/fsGroup so restricted-v2 admits the pods)
route.host "" OpenShift Route hostname
route.tlsTermination edge TLS termination: edge, passthrough, or reencrypt
secrets.existingSecretName "" Use a pre-created Secret instead of chart values
secrets.dbPassword "" PostgreSQL password (bundled DB mode)
secrets.redisPassword "" Redis password (bundled Redis mode)
secrets.secretKey "" JWT signing key (always required)
database.external false Use external DB instead of bundled TimescaleDB
redis.external false Use external Redis instead of bundled
image.registry ghcr.io Registry for the server image
api.replicas 1 Number of API pod replicas
worker.replicas 1 Number of Celery worker replicas
proxy.httpProxy "" HTTP proxy URL for outbound connections
proxy.httpsProxy "" HTTPS proxy URL for outbound connections
proxy.noProxy "localhost,127.0.0.1,.cluster.local" Hosts to bypass the proxy

Air-gap / internal registry

Mirror the chart and images into your internal registry, then override the image registries in your values file:

image:
  registry: registry.corp.internal
database:
  image: { registry: registry.corp.internal }
redis:
  image: { registry: registry.corp.internal }

Images to mirror: ghcr.io/sentari-dev/sentari/server, ghcr.io/sentari-dev/sentari/docs, docker.io/timescale/timescaledb:2.14.2-pg16, docker.io/redis:7.2-alpine. Pull the chart with helm pull oci://ghcr.io/sentari-dev/sentari/charts/sentari --version <release> and re-push it to your registry, or install from the local .tgz.

Air-gap licensing uses a license key with a signed max_offline_days claim.


Upgrading

helm upgrade sentari oci://ghcr.io/sentari-dev/sentari/charts/sentari \
  --version <new-release> \
  -f values.yaml \
  -n sentari

Database migrations run automatically on startup. Downgrading is not supported — always back up the database before upgrading.


Troubleshooting

Pods Pending / SCC errors. Confirm openshift.enabled=true is set. For the bundled DB path, prefer external DB/Redis if the StatefulSet pods fail admission.

oc describe pod -n sentari <pod-name>

API not healthy. Check logs for migration or configuration errors:

oc logs deploy/sentari-api -n sentari | grep -i "migration\|alembic\|error"

Common causes: placeholder secretKey; missing licenseKey (server boots read-only, writes return HTTP 402); wrong external DB/Redis URL.

Agent can't connect. Verify the Route is accessible: curl https://<route-host>/api/health. Check the agent log at /var/log/sentari/agent.log. If certificates are corrupted, re-enroll: delete /var/lib/sentari/certs/ and run with --enroll-token.


For additional assistance, contact your Sentari representative or visit the support portal.