# Red Hat OpenShift

Production-grade installation guide for Red Hat OpenShift 4.18 & newer. All snippets are idempotent and tested on OCP 4.18 (Kubernetes 1.31).

#### Table of Contents

1. [Overview](#id-1-overview)
2. [Prerequisites](#id-2-prerequisites)
3. [Quick-Start Variables](#id-2-prerequisites)
4. [Install Local Tooling](#id-2-prerequisites)
5. [OpenShift Cluster Preparation](#id-5-openshift-cluster-preparation)
6. [Install Cluster Add-ons](#id-6-install-cluster-add-ons)
7. [Provision PostgreSQL (CloudNativePG)](#id-7-provision-postgresql-cloudnativepg)
8. [Provision Object Storage](#id-8-provision-object-storage)
9. [Create Dynamiq Secrets](#id-9-create-dynamiq-secrets)
10. [Prepare Helm Values](#id-10-prepare-helm-values)
11. [Deploy Dynamiq](#id-11-deploy-dynamiq)
12. [Validate & Smoke-Test](#id-12-validate-and-smoke-test)
13. [Upgrade Workflow](#id-13-upgrade-workflow)
14. [Cleanup](#id-14-cleanup)
15. [Appendix A – values-ocp.yaml Reference](#id-15-appendix-a-values-ocp.yaml-reference)

### 1 – Overview

Dynamiq is a **low-code GenAI operating platform**.\
This guide deploys it as **fully private, TLS-only, auto-upgradeable** workloads on an OpenShift 4.18+ cluster.

Key building blocks:

* **CloudNativePG** (Crunchy Data) for HA PostgreSQL
* **Object storage** – AWS S3, MinIO, or OpenShift Data Foundation
* **External Secrets Operator** for secret sync
* **Fission** for serverless logic
* **Helm** for application lifecycle

### 2 – Prerequisites

| Requirement         | Details                                               |
| ------------------- | ----------------------------------------------------- |
| **OpenShift 4.18+** | Kubernetes 1.31, cluster-admin rights                 |
| **CPU workers**     | ≥ 2 × vCPU / 8 GiB (m5.large-class)                   |
| **GPU workers**     | *Optional* – G5/A10G for model inference              |
| **CLI tools**       | `oc` 4.18+, `helm` 3.14+, `jq`, `openssl`, `envsubst` |
| **Domain**          | Root/sub-domain delegated in DNS                      |
| **Outbound 443**    | Pull container images & reach S3/MinIO                |

### 3 – Quick-Start Variables

> Edit the first block only, then paste the rest *as one*.

```bash
# ---------- BEGIN USER CONFIG ----------
export BASE_DOMAIN="example.com"        # root or delegated sub-domain
export CLUSTER_NAME="dynamiq"           # DNS-safe
export PROJECT="dynamiq"                # OpenShift namespace
export REGION="us-east-1"               # for AWS snippets
# ---------- END USER CONFIG ------------

export OCP_VERSION="4.18"
export K8S_VERSION="1.31"
export STORAGE_S3_BUCKET="${CLUSTER_NAME}-data-$(openssl rand -hex 4)"
export DYNAMIQ_CHART_REPO="oci://709825985650.dkr.ecr.us-east-1.amazonaws.com/dynamiq/enterprise/dynamiq"
```

### 4 – Install Local Tooling

```bash
# OpenShift CLI
curl -LO "https://mirror.openshift.com/pub/openshift-v4/x86_64/clients/ocp/${OCP_VERSION}/openshift-client-linux.tar.gz"
tar -xzvf openshift-client-linux.tar.gz -C /usr/local/bin oc kubectl

# Helm
curl https://raw.githubusercontent.com/helm/helm/main/scripts/get-helm-3 | bash

# Verify
oc version --client
helm version
```

### 5 – OpenShift Cluster Preparation

1. **Log in**

   ```bash
   oc login https://api.${CLUSTER_NAME}.${BASE_DOMAIN}:6443 \
     --username kubeadmin --password <REDACTED>
   ```
2. **Create / select project**

   ```bash
   oc new-project ${PROJECT} || oc project ${PROJECT}
   ```
3. **(Optional) Create a fresh IPI cluster**

   ```bash
   openshift-install create cluster --dir ./install --log-level=info
   ```

### 6 – Install Cluster Add-ons

#### 6.1 External Secrets Operator

```bash
oc apply -f https://raw.githubusercontent.com/external-secrets/external-secrets/v0.9.4/deploy/crds/bundle.yaml

helm repo add external-secrets https://charts.external-secrets.io
helm upgrade --install external-secrets external-secrets/external-secrets \
  --namespace external-secrets --create-namespace \
  --set installCRDs=false \
  --wait
```

Create a `ClusterSecretStore` pointing at AWS Secrets Manager (swap provider if required):

```bash
cat <<EOF | envsubst | oc apply -f -
apiVersion: external-secrets.io/v1
kind: ClusterSecretStore
metadata:
  name: dynamiq
spec:
  provider:
    aws:
      service: SecretsManager
      region: $REGION
EOF
```

#### 6.2 Fission Serverless Engine

```bash
kubectl create -k "github.com/fission/fission/crds/v1?ref=v1.20.5"

helm repo add fission https://fission.github.io/fission-charts/
helm upgrade --install fission fission/fission-all \
  --namespace dynamiq-fission --create-namespace \
  --set routerServiceType=ClusterIP \
  --set defaultNamespace=${PROJECT} \
  --set analytics=false \
  --wait
```

#### 6.3 GPU MachineSets (optional)

```bash
oc adm machine-sets create-gpu \
  --accelerator-type nvidia-g5 \
  --name gpu-g5 \
  --replicas 1 \
  --cluster ${CLUSTER_NAME}
```

### 7 – Provision PostgreSQL (CloudNativePG)

```bash
oc apply -f https://get.crunchydata.com/postgres-operator/crunchy-postgres-operator.yaml
# Wait for operator to be Ready

cat <<EOF | oc apply -f -
apiVersion: postgresql.cnpg.io/v1
kind: Cluster
metadata:
  name: dynamiq-pg
  namespace: ${PROJECT}
spec:
  instances: 2
  imageName: registry.developers.crunchydata.com/crunchydata/crunchy-postgres:16
  storage:
    size: 50Gi
EOF
```

Extract connection details:

```bash
export DATABASE_HOST="dynamiq-pg-rw.${PROJECT}.svc.cluster.local"
export DATABASE_PORT="5432"
export DATABASE_NAME="postgres"
export DATABASE_USERNAME="postgres"
export DATABASE_PASSWORD="$(oc -n ${PROJECT} get secret dynamiq-pg-superuser -o jsonpath='{.data.password}' | base64 -d)"
```

### 8 – Provision Object Storage

#### Option A – AWS S3

```bash
aws s3api create-bucket \
  --bucket "${STORAGE_S3_BUCKET}" \
  --region "${REGION}" \
  --create-bucket-configuration LocationConstraint="${REGION}"

export STORAGE_SERVICE="s3"
```

#### Option B – Internal MinIO

```bash
helm repo add minio https://charts.min.io
helm upgrade --install minio minio/minio \
  --namespace storage --create-namespace \
  --set accessKey=minio --set secretKey=minio123 \
  --set buckets[0].name=${STORAGE_S3_BUCKET} \
  --wait

export STORAGE_SERVICE="minio"
```

### 9 – Create Dynamiq Secrets

```bash
export AUTH_ACCESS_TOKEN_KEY=$(openssl rand -base64 48 | tr -d '\n')
export AUTH_REFRESH_TOKEN_KEY=$(openssl rand -base64 48 | tr -d '\n')
export AUTH_VERIFICATION_TOKEN_KEY=$(openssl rand -base64 48 | tr -d '\n')

cat <<EOF | envsubst | oc apply -f -
apiVersion: v1
kind: Secret
metadata:
  name: nexus-secret
  namespace: ${PROJECT}
type: Opaque
stringData:
  DATABASE_HOST: "$DATABASE_HOST"
  DATABASE_PORT: "$DATABASE_PORT"
  DATABASE_SSLMODE: "require"
  DATABASE_NAME: "$DATABASE_NAME"
  DATABASE_USERNAME: "$DATABASE_USERNAME"
  DATABASE_PASSWORD: "$DATABASE_PASSWORD"
  STORAGE_SERVICE: "$STORAGE_SERVICE"
  STORAGE_S3_BUCKET: "$STORAGE_S3_BUCKET"
  AUTH_ACCESS_TOKEN_KEY: "$AUTH_ACCESS_TOKEN_KEY"
  AUTH_REFRESH_TOKEN_KEY: "$AUTH_REFRESH_TOKEN_KEY"
  AUTH_VERIFICATION_TOKEN_KEY: "$AUTH_VERIFICATION_TOKEN_KEY"
  # --- OPTIONAL TOKENS ---
  HUGGING_FACE_ACCESS_TOKEN: "<HF_TOKEN>"
  OPENAI_API_KEY: "<OPENAI_KEY>"
  SMTP_HOST: "<SMTP_HOST>"
  SMTP_USERNAME: "<SMTP_USER>"
  SMTP_PASSWORD: "<SMTP_PASS>"
EOF
```

### 10 – Prepare Helm Values

```bash
cat <<EOF > values-ocp.yaml
dynamiq:
  domain: ${BASE_DOMAIN}

nexus:
  image:
    repository: 709825985650.dkr.ecr.us-east-1.amazonaws.com/dynamiq/enterprise/nexus
  ingress:
    enabled: true
  externalSecrets:
    enabled: false
  appSecret: nexus-secret
  configMapData:
    STORAGE_SERVICE: ${STORAGE_SERVICE}
    STORAGE_S3_BUCKET: ${STORAGE_S3_BUCKET}

synapse:
  image:
    repository: 709825985650.dkr.ecr.us-east-1.amazonaws.com/dynamiq/enterprise/synapse
  ingress:
    enabled: true
  configMapData:
    STORAGE_SERVICE: ${STORAGE_SERVICE}
    STORAGE_S3_BUCKET: ${STORAGE_S3_BUCKET}

catalyst:
  image:
    repository: 709825985650.dkr.ecr.us-east-1.amazonaws.com/dynamiq/enterprise/catalyst
  configMapData:
    STORAGE_SERVICE: ${STORAGE_SERVICE}
    STORAGE_S3_BUCKET: ${STORAGE_S3_BUCKET}

ui:
  image:
    repository: 709825985650.dkr.ecr.us-east-1.amazonaws.com/dynamiq/enterprise/ui
  ingress:
    enabled: true
EOF
```

### 11 – Deploy Dynamiq

```bash
# Authenticate to ECR
aws ecr get-login-password --region us-east-1 | \
  helm registry login --username AWS --password-stdin 709825985650.dkr.ecr.us-east-1.amazonaws.com

# Install / upgrade Dynamiq
helm upgrade --install dynamiq ${DYNAMIQ_CHART_REPO} \
  --namespace ${PROJECT} \
  --values values-ocp.yaml \
  --wait
```

### 12 – Validate & Smoke-Test

```bash
oc -n ${PROJECT} get route
```

Create **CNAME** records (`app`, `api`, etc.) pointing to the OpenShift router host.

* `https://app.${BASE_DOMAIN}` → Dynamiq UI
* `https://api.${BASE_DOMAIN}` → Dynamiq API

First registered user gains **Admin** rights.

### 13 – Upgrade Workflow

```bash
helm repo update
helm upgrade dynamiq ${DYNAMIQ_CHART_REPO} \
  --namespace ${PROJECT} \
  --reuse-values \
  --wait
```

Provided each component runs ≥ 2 replicas, OpenShift performs zero-downtime rolling updates.

### 14 – Cleanup

```bash
helm uninstall dynamiq -n ${PROJECT} || true
helm uninstall fission -n dynamiq-fission || true
oc delete project ${PROJECT} dynamiq-fission external-secrets || true
aws s3 rb s3://${STORAGE_S3_BUCKET} --force          # if you used AWS S3
# If you created the cluster via step 5:
openshift-install destroy cluster --dir ./install
```

### 15 – Appendix A – values-ocp.yaml Reference

| Key                  | Description             | Required |
| -------------------- | ----------------------- | -------- |
| `dynamiq.domain`     | Base public domain      | ✔        |
| `nexus.appSecret`    | Secret created in §9    | ✔        |
| `*_image.repository` | ECR path                | ✔        |
| `STORAGE_SERVICE`    | `s3`, `minio`, or `odf` | ✔        |

#### Need help? Reach out to us - <support@getdynamiq.ai>

**Happy shipping Dynamiq on OpenShift!** 🚀
