Skip to main content

Platform Choice

The chart supports Gateway API, Kubernetes Ingress, and OpenShift Routes for the user-facing app/API path. Enable one depending on your platform.
PlatformMechanismValues key
Kubernetes, preferredGateway API with Envoy Gatewaygateway.enabled
Kubernetes, nginxIngressingress.enabled
OpenShiftRouteopenshift.route.enabled

Kubernetes — Gateway API with Envoy Gateway

Gateway API is the preferred option for the kOps v2 rehearsal profile and for self-hosted clusters that already run Envoy Gateway.

Prerequisites

Install Gateway API CRDs, cert-manager with Gateway support, and Envoy Gateway:
kubectl apply --server-side -f \
  https://github.com/kubernetes-sigs/gateway-api/releases/download/v1.4.1/standard-install.yaml

helm upgrade --install cert-manager oci://quay.io/jetstack/charts/cert-manager \
  --version v1.20.2 \
  --namespace cert-manager \
  --create-namespace \
  --set crds.enabled=true \
  --set config.enableGatewayAPI=true

helm upgrade --install eg oci://docker.io/envoyproxy/gateway-helm \
  --version v1.7.3 \
  --namespace envoy-gateway-system \
  --create-namespace
Create or reuse a GatewayClass named eg. On AWS kOps, the kOps v2 reference configures Envoy’s data-plane Service as an internet-facing NLB.

Values

ingress:
  enabled: false

gateway:
  enabled: true
  gatewayClass:
    create: false
    name: eg
  annotations:
    cert-manager.io/cluster-issuer: letsencrypt-prod
  tls:
    enabled: true
    app:
      secretName: cobi-dashboard-app-tls
    api:
      secretName: cobi-dashboard-api-tls
    connect:
      secretName: cobi-dashboard-connect-tls
  app:
    enabled: true
    hostname: app.example.com
  api:
    enabled: true
    hostname: api.example.com
    timeouts:
      request: 0s
      backendRequest: 0s
  connect:
    enabled: true
    hostname: connect.example.com
    timeouts:
      request: 0s
      backendRequest: 0s
  minio:
    enabled: false
  grafana:
    enabled: false
Set the corresponding application URLs:
# Frontend
--from-literal=VITE_API_BASE_URL="https://api.example.com"
--from-literal=VITE_API_SANDBOX_URL="https://api.example.com"

# Backend
--from-literal=BETTER_AUTH_URL="https://api.example.com"
--from-literal=FRONTEND_URL="https://app.example.com"
--from-literal=CONNECT_SERVICE_URL="http://cobi-dashboard-connect:8000"

Verify

kubectl get gateway -n cobi
kubectl get httproute -n cobi
kubectl get svc -n envoy-gateway-system

Kubernetes — Ingress

The frontend and backend each get their own Ingress and their own hostname. The frontend makes API calls to the backend hostname directly — there is no /api path proxying.

Prerequisites

Install ingress-nginx before deploying the chart:
helm repo add ingress-nginx https://kubernetes.github.io/ingress-nginx
helm repo update

helm install ingress-nginx ingress-nginx/ingress-nginx \
  --namespace ingress-nginx \
  --create-namespace \
  --set controller.replicaCount=2
Wait for the LoadBalancer IP or hostname:
kubectl get svc -n ingress-nginx ingress-nginx-controller \
  -o jsonpath='{.status.loadBalancer.ingress[0].ip}'
Create DNS A records for both hostnames pointing at this address.

Separate-host values

Ingress is configured under the top-level ingress block:
gateway:
  enabled: false

ingress:
  enabled: true
  className: nginx
  annotations: {}
  tls:
    - secretName: cobi-dashboard-tls
      hosts:
        - app.example.com
        - api.example.com
        - connect.example.com
  app:
    enabled: true
    host: app.example.com
  api:
    enabled: true
    host: api.example.com
  connect:
    enabled: true
    host: connect.example.com
  minio:
    enabled: false
  grafana:
    enabled: false
Set the corresponding URLs in the frontend and backend Secrets:
# Frontend — points at the backend hostname
--from-literal=VITE_API_BASE_URL="https://api.example.com"

# Backend — points at itself, the frontend, and the in-cluster Connect service
--from-literal=BETTER_AUTH_URL="https://api.example.com"
--from-literal=FRONTEND_URL="https://app.example.com"
--from-literal=CONNECT_SERVICE_URL="http://cobi-dashboard-connect:8000"

Shared-host values

Shared-host mode routes the frontend at / and the backend at ingress.apiPrefix:
ingress:
  enabled: true
  className: nginx
  host: cobi.example.com
  apiPrefix: /api
  tls:
    - secretName: cobi-dashboard-tls
      hosts:
        - cobi.example.com
helm repo add jetstack https://charts.jetstack.io
helm install cert-manager jetstack/cert-manager \
  --namespace cert-manager \
  --create-namespace \
  --set crds.enabled=true
# cluster-issuer.yaml
apiVersion: cert-manager.io/v1
kind: ClusterIssuer
metadata:
  name: letsencrypt-prod
spec:
  acme:
    server: https://acme-v02.api.letsencrypt.org/directory
    email: [email protected]
    privateKeySecretRef:
      name: letsencrypt-prod
    solvers:
      - http01:
          ingress:
            ingressClassName: nginx
kubectl apply -f cluster-issuer.yaml
Add the annotation under the top-level ingress block:
ingress:
  annotations:
    cert-manager.io/cluster-issuer: "letsencrypt-prod"
  tls:
    - secretName: cobi-dashboard-tls
      hosts:
        - app.example.com
        - api.example.com

TLS — Pre-existing certificates

kubectl create secret tls cobi-dashboard-tls --cert=tls.crt --key=tls.key --namespace cobi

OpenShift — Frontend and API Routes

OpenShift uses Routes instead of Ingress. The chart creates Routes for the frontend and backend API. Connect stays internal through CONNECT_SERVICE_URL unless your deployment needs a public Connect endpoint.

Values

ingress:
  enabled: false

gateway:
  enabled: false

# Enable OpenShift Routes
openshift:
  route:
    enabled: true
    host: dashboard.apps.cluster.example.com
    api:
      enabled: true
      host: api.apps.cluster.example.com
The Routes use edge TLS termination — TLS is terminated at the OpenShift router, plain HTTP to the pods.

Generated Route resources

apiVersion: route.openshift.io/v1
kind: Route
metadata:
  name: cobi-dashboard-frontend
  namespace: cobi
spec:
  host: dashboard.apps.cluster.example.com
  to:
    kind: Service
    name: cobi-dashboard-frontend
  port:
    targetPort: http
  tls:
    termination: edge
---
apiVersion: route.openshift.io/v1
kind: Route
metadata:
  name: cobi-dashboard-backend
  namespace: cobi
spec:
  host: api.apps.cluster.example.com
  to:
    kind: Service
    name: cobi-dashboard-backend
  port:
    targetPort: http
  tls:
    termination: edge
Set the corresponding URLs in the frontend and backend Secrets:
# Frontend — points at the backend route hostname
--from-literal=VITE_API_BASE_URL="https://api.apps.cluster.example.com"

# Backend — use the public API URL created for your backend
--from-literal=BETTER_AUTH_URL="https://api.apps.cluster.example.com"
--from-literal=FRONTEND_URL="https://dashboard.apps.cluster.example.com"

Observability (Grafana)

Expose the Grafana UI from the otel-lgtm component when otelLgtm.enabled: true:
ingress:
  enabled: true
  grafana:
    enabled: true
    host: grafana.example.com

Internal Service Addresses

When configuring Secrets, use these ClusterIP hostnames (release name cobi-dashboard, namespace cobi):
ServiceInternal address
Backendcobi-dashboard-backend:3000
Frontendcobi-dashboard-frontend:8080
Connectcobi-dashboard-connect:8000
PostgreSQLcobi-dashboard-postgresql:5432
Qdrantcobi-dashboard-qdrant:6333
MinIO (API)cobi-dashboard-minio:9000
MinIO (Console)cobi-dashboard-minio:9001
vLLMcobi-dashboard-vllmstack-engine:8000