Skip to content

Deploy with Helm

This guide installs Release 2 of the Open Operator Platform (OpenOP) with the umbrella Helm chart oop-platform-chart. It covers two cases:

  • a local kind (Kubernetes in Docker) cluster, set up by one script — the quickest way to try the platform;
  • an existing Kubernetes cluster.

Both use the same chart. The kind setup is meant for local development, integration testing and demos, not for production. Every option below links to the Helm README, which lists all values.

What gets installed

Component What it does
Open Exposure Gateway (OEG) Northbound entry point for App Providers (CAMARA APIs)
Service Resource Manager (SRM) Deploys apps on edge clusters and drives the network through TF-SDK control paths
Federation Manager (FM) Federation with other operators (GSMA OPG.04 EWBI)
Keycloak Login and tokens: realm oop (portal users, App Providers, OEG) and realm federation (FM partners)
PostgreSQL One shared instance, one database per component
NATS Message bus between OEG, SRM and FM
AI² MCP and AI² AI Agent CAMARA tools for AI agents, and a natural-language interface backed by Groq
Portal Developer portal (web UI and its backend)
Data Lab Dataspace metadata catalogue. Off by default, see Optional: Data Lab

Release 2 has no MongoDB and no Artefact Manager: all data lives in PostgreSQL. To move from 1.x, see Upgrading from 1.x.

Prerequisites

Software

Tool Minimum version Notes
Docker 20.x kind runs Kubernetes nodes in Docker containers
kind 0.20 Install kind (kind only)
kubectl 1.25 Install kubectl
Helm v3 Install Helm
Bash 4+ Needed by the deploy scripts

Check your installations:

docker --version
kind --version
kubectl version --client
helm version

Hardware

For a comfortable local run of the full platform:

Resource Minimum Recommended
CPU 4 cores 6+ cores
RAM 12 GiB 16 GiB
Disk 20 GiB free 30+ GiB free

Plan for more if you turn on the Data Lab, which adds Elasticsearch and Virtuoso.

Get the charts

Clone the Helm repository at the release tag. All commands on this page run from its root, the folder that contains deploy-on-kind.sh:

git clone https://labs.etsi.org/rep/oop/deploy/helm.git
cd helm
git checkout v2.0.0-release
ls deploy-on-kind.sh oop-platform-chart environments/kind

Deploy on kind

1. (Optional) Add a Groq API key for AI²

AI²'s agent needs a Groq API key for its LLM calls. Create environments/kind/secrets.values.yaml (it is gitignored) before deploying:

ai2:
  secrets:
    groqApiKey: "<your-groq-api-key>"

Without this file the deploy script warns and carries on; AI² is installed but can't make LLM calls.

You don't need to put any other secret here. Database passwords, Keycloak passwords and client secrets, and the OEG and FM encryption keys are generated on first install and kept across upgrades. See Secrets in the Helm README to pin your own values.

2. Run the deploy script

./deploy-on-kind.sh

The script runs two steps, which you can also run one at a time:

  1. scripts/kind-bootstrap.sh (once per cluster) — checks that kind and kubectl are installed, creates the kind cluster oop-cluster from environments/kind/cluster.yaml, waits for the node and creates namespace oop. If the cluster already exists, it asks whether to delete and recreate it.
  2. scripts/helm-deploy.sh (safe to re-run) — loads secrets.values.yaml if it exists, downloads the NATS chart, runs the install below, and prints the access URLs:

    helm upgrade --install oop-platform ./oop-platform-chart \
      -n oop --create-namespace \
      -f environments/kind/values.yaml \
      -f environments/kind/secrets.values.yaml   # only if the file exists
    

After a few minutes all pods should be running.

3. Tell SRM where apps are reached

On kind, environments/kind/values.yaml gives SRM a ready-made setup: one Kubernetes control path, k8s-local, that deploys apps into namespace oop-apps of the kind cluster itself, and one zone, kind-local.

The control path's endpoint_host is a placeholder, <IP>. SRM puts this address in the endpoints of deployed apps, so replace it with the address the apps' NodePorts are reached on, usually the kind node's IP:

docker inspect -f '{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}' oop-cluster-control-plane

Edit endpoint_host under srm.topology in environments/kind/values.yaml, then re-run ./scripts/helm-deploy.sh. To describe your own zones and clusters, see Define edge zones.

How the values files combine

Helm merges values files in order; later files win:

  1. oop-platform-chart/values.yaml — base defaults for all components.
  2. environments/kind/values.yaml — kind overrides: NodePorts, global.publicHost: localhost, the SRM control path and topology, sample users.
  3. environments/kind/secrets.values.yaml — your secrets (gitignored).

The kind NodePorts:

Component Value NodePort
SRM srm.service.nodePort 32415
OEG oeg.service.nodePort 32263
Federation Manager federation-manager.service.nodePort 30989
Keycloak global.nodePorts.keycloak 30081
Portal global.nodePorts.portal 30082
Data Lab UI global.nodePorts.datalab 30083
AI² MCP / AI Agent ai2.mcp.service.nodePort / ai2.aiAgent.service.nodePort 32004 / 32013

Changing a NodePort means recreating the kind cluster

Each NodePort must match a containerPort in environments/kind/cluster.yaml. If you change one, update both files, delete the cluster (kind delete cluster --name oop-cluster) and run ./deploy-on-kind.sh again: kind only applies port mappings when it creates a cluster.

Check it worked

Pods

kubectl get pods -n oop

All pods should reach Running (or Completed for setup jobs).

Pod name starts with Component
oegcontroller Open Exposure Gateway
srmcontroller Service Resource Manager
federation-manager Federation Manager
keycloak Keycloak
postgres PostgreSQL
oop-nats NATS
ai2-mcp, ai2-ai-agent, ai2-redis AI² (MCP server, agent, and the agent's session store)
portal Portal
datalab- Data Lab (only when enabled)

Access URLs

On kind:

Service URL
Portal http://localhost:30082
OEG API (Swagger) http://localhost:32263/docs
SRM API (Swagger) http://localhost:32415/docs
Keycloak http://localhost:30081
Keycloak admin console http://localhost:30081/admin (user admin, password below)
Federation Manager (Swagger) http://localhost:30989/docs
AI² MCP (for MCP clients) http://localhost:32004/mcp
AI² AI Agent (Swagger) http://localhost:32013/docs
Data Lab (when enabled) http://localhost:30083

Quick check that OEG is up and can reach its dependencies:

curl -s http://localhost:32263/platform/healthz
curl -s http://localhost:32263/platform/readyz

First login

Every install creates a platform administrator, oop-admin, with a temporary password; the first login asks for a new one. On kind there are also three sample users in organization dev-org, one per role: dev-provider (App Provider), dev-operator (operator) and dev-admin (platform admin). Read the passwords with:

# Keycloak admin console (user: admin)
kubectl -n oop get secret keycloak-credentials -o jsonpath='{.data.admin-password}' | base64 -d; echo
# oop-admin's first-login password
kubectl -n oop get secret keycloak-credentials -o jsonpath='{.data.initial-admin-password}' | base64 -d; echo
# sample users (kind only)
kubectl -n oop get secret keycloak-credentials -o jsonpath='{.data.dev-user-password}' | base64 -d; echo

Log in to the Portal with one of these users. Roles, API products and organizations are covered in Manage users and access; what to do next is in the Tutorials.

Optional: Data Lab

The Data Lab metadata catalogue is off by default. It needs two settings, because the Data Lab and its Keycloak realm dataspace live in different subcharts:

Setting What it does
datalab.enabled=true Deploys the Data Lab (piveau hub and search, Virtuoso, Elasticsearch, the Data Lab UI and API)
keycloak.realms.dataspace.enabled=true Creates realm dataspace in Keycloak, which the Data Lab uses for login

Set both. With only the first, nobody can log in and the Data Lab pods wait for a realm that never comes. With only the second, the realm exists and nothing uses it.

On a running platform (from the helm directory):

helm upgrade oop-platform ./oop-platform-chart -n oop --reuse-values \
  --set datalab.enabled=true --set keycloak.realms.dataspace.enabled=true

Or set both to true in your values file. Turning it on in an existing installation restarts Keycloak, which then imports the realm. The Data Lab needs login: it is not deployed when global.auth.enabled=false.

Once the pods are Running, the Data Lab UI is at http://localhost:30083. On kind, the realm has the sample users steward, alice and bob, who share the dev-user-password (see First login).

Set the base URI before the first install

datalab.baseUri mints every catalogue, dataset and distribution URI, and these are persistent identifiers. The default embeds the NodePort address and suits kind only, so set your own before the first install and never change it.

All Data Lab options, and how to reset it, are in the Data Lab section of the Helm README. To use it, continue with Publish a dataset in the Data Lab.

Optional: try OpenOP without login

On your own machine you can switch login off for the whole platform:

helm upgrade --install oop-platform ./oop-platform-chart -n oop \
  -f environments/kind/values.yaml --set global.auth.enabled=false

Keycloak, Federation Manager and the Data Lab are then not deployed. The chart refuses this setting unless global.publicHost is localhost or 127.0.0.1, because without login anyone who reaches the Portal or OEG can deploy workloads on the cluster.

Deploy on an existing cluster

You need kubectl pointing at your cluster and Helm v3. kind and deploy-on-kind.sh are not used.

There is no service account or token to create for SRM, and no values are required to install. From the root of the Helm repository:

helm dependency update ./oop-platform-chart
helm upgrade --install oop-platform ./oop-platform-chart -n oop --create-namespace \
  --set global.publicHost=10.20.30.40    # optional, see below

Before you rely on it, go through these points. Each links to the part of the README that explains it:

  • Public address. Keycloak's and the Portal's public URLs are built from global.publicHost. If you leave it empty, it is detected at install from the first node (its ExternalIP, else its InternalIP). Set it when that is not the address users reach, and always when you use helm template or GitOps tools such as Argo CD. See Install on another cluster.
  • What is reachable from outside. Only Keycloak (30081) and the Portal (30082) get NodePorts by default. To reach OEG, SRM or Federation Manager from outside, set their service.type and service.nodePort, as environments/kind/values.yaml does.
  • SRM zones and clusters. SRM has no control paths or topology by default, so it can't deploy anything until you set srm.topology and srm.workloads.namespaces. See Define edge zones and the SRM chart README.
  • Storage. Choose postgresql.persistence (size and storage class; run kubectl get storageclass to list yours) before the first install: Kubernetes can't change it in place later.
  • Federation. Partners must reach FM's callback links: set federation-manager.publicUrl. See the Federation Manager chart README.
  • AI². Pass the Groq key with a secrets file (-f my-secrets.values.yaml), as in step 1.

Put your settings in your own values file (for example environments/my-cluster/values.yaml) and pass it with -f.

Upgrade

Re-run ./scripts/helm-deploy.sh, or:

helm upgrade oop-platform ./oop-platform-chart -n oop \
  -f environments/kind/values.yaml

As with the Data Lab, add -f environments/kind/secrets.values.yaml if you created it in step 1.

Generated passwords, client secrets and encryption keys are read back from their Secrets and kept.

Back up the encryption keys

OEG (oegcontroller-keys) and Federation Manager (federation-manager-keys) encrypt secrets they store in the database. Back these Secrets up together with the database and never change them: data encrypted with the old key can't be read with a new one. See Encryption keys.

Upgrading from 1.x

An in-place upgrade from 1.x is not supported. 1.x kept its data in MongoDB; 2.0 uses PostgreSQL and does not migrate the old data. Install 2.0 fresh. What changed in each component is in the Release 2.0.0 notes.

On kind, the simplest is to start over, since cluster.yaml changed too:

kind delete cluster --name oop-cluster
sudo rm -rf /tmp/kind-oop        # 1.x MongoDB data on the host
./deploy-on-kind.sh

On a cluster you keep, save what you need from MongoDB, uninstall the 1.x release, remove leftover MongoDB volumes and the old oop-user service account, then install 2.0. The exact commands are in Upgrading from 1.x.

Troubleshooting

A pod doesn't start or keeps crashing

kubectl get pods -n oop
kubectl describe pod <pod-name> -n oop       # events, image pulls, resource limits
kubectl logs <pod-name> -n oop               # current logs (add -f to follow)
kubectl logs <pod-name> -n oop --previous    # logs from before the last crash

For example, for OEG or SRM:

kubectl logs -f deployment/oegcontroller -n oop
kubectl logs -f deployment/srmcontroller -n oop

Helm release status

helm status oop-platform -n oop
helm list -n oop

Storage

kubectl get pvc -n oop
kubectl get pv

If the PostgreSQL claim stays Pending, check that the storage class in postgresql.persistence.storageClass exists in your cluster (kubectl get storageclass).

Using locally built images

The charts use pullPolicy: IfNotPresent, so you can load an image you built into the cluster, then set the component's image.tag to your tag:

kind load docker-image <your-image> --name oop-cluster

Behind a corporate proxy or custom CA

If your network needs a custom CA certificate, see Mounting CA Certificates on kind in the Helm README.

Cleanup

helm uninstall oop-platform -n oop
kind delete cluster --name oop-cluster

helm uninstall keeps some things on purpose, so a reinstall finds them again: the PostgreSQL and NATS volumes, the Secrets postgres-credentials, keycloak-credentials, portal-credentials, oegcontroller-keys and federation-manager-keys, SRM's app namespaces (e.g. oop-apps) and, if you turned on the Data Lab, its volumes and datalab-credentials. To remove everything on a cluster you keep:

kubectl delete namespace oop oop-apps