Install with Helm
Deploy the AIOE API and console to any Kubernetes cluster, with your own PostgreSQL and Redis.
The aioe Helm chart deploys the two AIOE services, the API and the console, with an ingress in front of them. It runs on any Kubernetes, in any cloud or on-premises, and contains nothing specific to one cloud. You bring PostgreSQL and Redis, managed or in the cluster.
Before you start
- A Kubernetes cluster,
kubectland Helm 3. - An ingress controller. The chart defaults to ingress-nginx and sets the annotations that stop it buffering or timing out the relay's long-lived streams. With another controller, set the same behaviour yourself (see Proxies and load balancers).
- Two host names with a TLS certificate covering both, for example
aioe.example.com(API) andconsole.aioe.example.com(console), stored as a Kubernetes TLS secret. - A PostgreSQL database for AIOE, and a user that can create tables in it. The reference layouts use PostgreSQL 16.
- A Redis instance. The chart runs two API replicas, and more than one replica needs Redis.
- The AIOE chart and access to the
aioe-apiandaioe-consoleimages, from your AIOE release. The console image has the API's public address built into it, so it must be built for yourapi.publicUrl. Ask Mojo Up if your release does not include one. - Your two app registrations, from Connect your identity provider.
Install
Create the namespace and the API's secret
The API reads every key of one Kubernetes secret as an environment variable. Put the connection strings and the platform's keys in it:
kubectl create namespace aioe
kubectl -n aioe create secret generic aioe-api \
--from-literal=DATABASE_URL='postgresql://aioe:<password>@<postgres-host>:5432/aioe?sslmode=require' \
--from-literal=REDIS_URL='rediss://:<password>@<redis-host>:<port>' \
--from-literal=AIOE_SIGNING_KEY="$(node -e "crypto.subtle.generateKey({name:'Ed25519'},true,['sign']).then(k=>crypto.subtle.exportKey('jwk',k.privateKey)).then(j=>console.log(JSON.stringify(j)))")" \
--from-literal=AIOE_SECRETS_KEY="$(openssl rand -base64 32)"AIOE_SIGNING_KEYsigns requests the platform sends over the overlay, and every replica must share it. Without it, each replica makes its own key at start.AIOE_SECRETS_KEYseals the secrets, linked accounts and workbench backups the platform keeps. Without it, the platform keeps none of them.
The signing key command needs Node.js 20 or later. Keep a copy of both keys in your secret store. Platform keys and rotation explains each one. Use redis:// for Redis without TLS.
Write your values
Create values.yaml with what differs from the defaults. Every value is listed in Helm values.
image:
registry: <your registry>
tag: <release tag>
api:
publicUrl: https://aioe.example.com
consoleUrl: https://console.aioe.example.com
existingSecret: aioe-api
env:
AIOE_ENVIRONMENT: production
# OTEL_EXPORTER_OTLP_ENDPOINT: http://otel-collector:4318
bootstrapTenant:
enabled: true
slug: example
organisation: Example Pty Ltd
# Your email domains. Add the API's host name if it is not under one of them.
domains: example.com
oidcIssuer: https://login.microsoftonline.com/<tenant-id>/v2.0
oidcAudience: <API registration client ID>
oidcClientId: <client registration client ID>
apiScope: api://aioe.example.com/access
ingress:
apiHost: aioe.example.com
consoleHost: console.aioe.example.com
tls:
- secretName: aioe-tls
hosts: [aioe.example.com, console.aioe.example.com]Replace the bootstrap values
The chart's defaults for bootstrapTenant are Mojo Up's own organisation and Entra registrations. Always set your own.
Install the chart
From the folder that holds the chart:
helm install aioe ./aioe -n aioe -f values.yaml
kubectl -n aioe rollout status deployment/aioe-apiThe release name (aioe here) prefixes every resource: the deployments and services are aioe-api and aioe-console. Each API pod applies any pending database migrations before it starts serving, then reports ready on /readyz.
Check it
kubectl -n aioe logs deployment/aioe-api | grep -E 'postgres|bootstrap|broker|listening'
curl https://aioe.example.com/healthz
curl https://aioe.example.com/.well-known/aioe.jsonThe log should say connected to postgres, bootstrap tenant ready, relay broker: redis (multi-replica) and aioe api listening. /healthz answers with the API's version, and the discovery document names your organisation.
Open https://console.aioe.example.com and sign in.
Publish the discovery record
Add the aioe-discover record on each email domain so workbenches find AIOE from a work email. See Publish the discovery record.
What the chart deploys
| Resource | Name | Notes |
|---|---|---|
| Deployment and Service | <release>-api | Container port 3001, service port 80. Readiness probe /readyz every 10 s, liveness probe /healthz every 30 s. Runs as non-root with a read-only root file system and no added capabilities. |
| Deployment and Service | <release>-console | Container port 8080, service port 80. Readiness probe /healthz every 10 s. |
| Ingress | <release> | Routes ingress.apiHost to the API and ingress.consoleHost to the console, with the TLS settings you give. |
The chart does not deploy PostgreSQL, Redis, or the overlay's relay and gateway. To add the overlay, see Overlay and relays.
Proxies and load balancers
Workbenches keep a Server-Sent Events stream open to the API, and remote control travels down it. Anything between the internet and the API must:
- not buffer responses (the chart sets
nginx.ingress.kubernetes.io/proxy-buffering: "off"); - allow long reads (the chart sets
nginx.ingress.kubernetes.io/proxy-read-timeout: "3600", one hour; the stream reconnects after that).
Sticky sessions are not needed: with Redis, any replica can reach any workbench. See Redis for more than one replica.
Change a setting later
Edit values.yaml, or the secret, then:
helm upgrade aioe ./aioe -n aioe -f values.yamlA change to the secret alone does not restart the pods: run kubectl -n aioe rollout restart deployment/aioe-api after it. Upgrading to a new release is in Upgrades and backups.