Skip to main content

Installation

Step by step guides per method (Kubernetes with Helm, OS packages, Docker Compose, make, air-gapped site): Install the Navin Platform.

1. Local stack (Docker Compose)​

cd deploy/compose
cp .env.example .env # replace every CHANGE_ME_ value (openssl rand -hex 32)
docker compose config -q
docker compose up -d
docker compose --profile gpu up -d vllm # NVIDIA GPU option
docker compose --profile ollama up -d ollama # CPU option

migrate runs python -m eai.migrate before the control plane starts. Keycloak imports realm enterprise-ai from deploy/keycloak/realm-enterprise-ai.json. OPA loads the deploy/policies/opa bundle. seaweedfs-init creates, through the S3 API of SeaweedFS, the WORM bucket eai-audit-worm (object lock, COMPLIANCE mode), eai-artifacts and opik.

First portal account: EAI_BOOTSTRAP_ADMIN_USERNAME and EAI_BOOTSTRAP_ADMIN_PASSWORD of .env (navin / navin by default). The control plane creates it at its first start on the tenant EAI_BOOTSTRAP_TENANT with the roles platform_admin and tenant_admin; at the first sign in (http://localhost:3300, Login tab) the portal requires a new password and the installation password stops working. A restart never resets this account. On Kubernetes, put both variables in the control plane secret for the first sign in, then remove them. In production (EAI_ENVIRONMENT=prod), the control plane refuses to start with a weak installation password (navin, the login, under 12 characters, without letters and digits): choose a strong one-time value.

The Makefile at the root of the repository offers the same operations: make docker-env (.env file with random local secrets), make docker-start or make <service>-start service by service, make docker-status, make <service>-logs. See the README (README.en.md).

Load the OpenFGA model:

fga store create --name enterprise-ai --api-url http://localhost:8082
fga model write --store-id <id> --file deploy/policies/openfga/model.fga
python3 tools/policies/fga_build.py --check

2. On-prem Kubernetes with GPU (Helm + Argo CD)​

Requirements: Kubernetes >= 1.27, labelled NVIDIA GPU nodes, CSI storage, an S3 compatible store (Ceph RGW, SeaweedFS or the object storage of your infrastructure), Harbor.

  1. Operators with OpenTofu/Terraform: cd deploy/terraform/envs/onprem-gpu && tofu init -backend-config=backend.hcl && tofu plan -var kube_context=<dedicated-context> && tofu apply (namespaces, Cilium with WireGuard and SPIRE mutual auth, cert-manager, GPU Operator + DCGM, KEDA, Kyverno, CloudNativePG, Velero, Argo CD).
  2. OpenBao: deploy, then run deploy/openbao/bootstrap.sh (KV v2, transit, kubernetes auth, policies).
  3. SPIRE: deploy/spire/server.conf, agent.conf, registration-entries.yaml.
  4. GitOps: kubectl apply -f deploy/argocd/appproject.yaml -f deploy/argocd/repository-toolhive.yaml -f deploy/argocd/applicationset.yaml (repository-toolhive.yaml declares the OCI registry of the ToolHive charts, see mcp-runtime.md). Sync waves: infra (-3), data (-2), platform (-1), apps (0).
  5. Application chart without Argo CD:
    helm dependency build deploy/helm/enterprise-ai
    helm upgrade --install enterprise-ai deploy/helm/enterprise-ai -n enterprise-ai \
    -f deploy/helm/enterprise-ai/values-prod.yaml -f deploy/helm/enterprise-ai/values-onprem-gpu.yaml
    Each vllm.models entry creates a vLLM Deployment (nvidia.com/gpu limit, nodeSelector, tolerations) and a KEDA ScaledObject on vllm:num_requests_waiting.

3. Air-gapped install (Harbor mirror)​

  1. On a connected host list images (helm template ... | grep image: | sort -u plus operator images).
  2. Mirror with skopeo copy --all docker://vllm/vllm-openai:v0.33.0 docker://<harbor-host>/mirror/vllm/vllm-openai:v0.33.0, verify cosign signatures and re-sign with the internal key (Kyverno rule key-based-mirror).
  3. Charts: helm pull then helm push <chart>.tgz oci://<harbor-host>/charts. Set helm_repository_mirror (Terraform) and global.imageRegistry (Helm).
  4. Models: download weights offline and copy them to the cache PVC (HF_HUB_OFFLINE=1 is enforced in vLLM pods).
  5. Trivy DB: trivy --download-db-only and transfer ~/.cache/trivy.
  6. Keep provider egress disabled (do not set eai.io/provider-* labels); only local models are served.

GitOps deployment (Argo CD)​

One Kubernetes cluster per environment (dev, staging, prod). The Terraform module deploy/terraform/modules/platform-bootstrap installs the foundation (Cilium, cert-manager, Kyverno, KEDA, CloudNativePG, Velero, GPU operator, Argo CD). It also creates every platform namespace with its Pod Security level, then registers the local cluster in Argo CD with the label eai.io/environment (Secret cluster-in-cluster in the argocd namespace).

Argo CD then installs everything else from this repository: deploy/argocd/applicationset.yaml generates one Application per component for every cluster registered with that label. The ApplicationSet does not enable CreateNamespace: namespaces come from the Terraform module and from deploy/cilium/namespaces.yaml, with their Pod Security level. A component whose namespace is missing fails instead of running without that level. Every environment uses values-prod.yaml (the third-party services run with the same TLS values everywhere); the per-environment image tags come from deploy/gitops/<env>/image-tags.yaml.

Layer (eai.io/layer)Components
infraCilium and Kyverno policies, cert-manager issuers, KEDA, Velero, External Secrets, kube-prometheus-stack (Prometheus Operator CRDs), SPIRE, ToolHive CRDs
dataOpenBao, PostgreSQL (CloudNativePG), Redis, NATS, Qdrant, Temporal, ClickHouse, Keycloak, OpenFGA, OPA
platformAPISIX, LiveKit, Grafana Alloy, OpenTelemetry Collector, OpenSearch (logs and Security Analytics), Jaeger, Opik, alert rules, ToolHive Operator (MCP Runtime, mcp-runtime.md)
appschart deploy/helm/enterprise-ai (Control Plane, portal, workers, LiteLLM, vLLM)

Every third-party chart is pinned to an exact version in the ApplicationSet. Its values come from the repository, and the header of each values file gives the repository, the chart and the version. Every image comes from the Harbor mirror (<harbor-host>/mirror/...) with an exact tag.

Prerequisites before the first sync

  1. Harbor mirror filled with the images of every values file. Air-gapped, also publish the charts to <harbor-host>/charts (already allowed by the AppProject) and replace repo in the ApplicationSet.
  2. Secrets in OpenBao, synced by External Secrets: the expected names and keys are documented at the top of each values file. Full inventory and bootstrap order: secrets.md.
  3. S3-compatible object storage of your infrastructure (Ceph RGW or any S3 API), reachable over HTTPS: replace <s3-host> in EAI_S3_ENDPOINT, networkSecurity.objectStorageFQDNs (deploy/helm/enterprise-ai/values.yaml), the Opik values (attachments), Velero and the CloudNativePG backup. SeaweedFS only runs in the docker compose stack.

Installation

# 1. Foundation, once per cluster: module platform-bootstrap with environment = dev, staging or prod
terraform init && terraform apply
# 2. Argo CD project and ApplicationSet
kubectl -n argocd apply -f deploy/argocd/appproject.yaml -f deploy/argocd/repository-toolhive.yaml -f deploy/argocd/applicationset.yaml

In dev and staging, sync is automatic. A resource whose CRD or namespace comes from an earlier layer is retried (10 attempts, waiting up to 10 minutes) until it converges. On the first install, OpenBao starts sealed: initialise it as soon as it runs (docs/operations/secrets.md); the Secrets and the pods that need them follow on their own.

In prod, sync is manual, one layer after the other:

argocd app sync -l eai.io/environment=prod,eai.io/layer=infra
argocd app wait -l eai.io/environment=prod,eai.io/layer=infra --health --timeout 1800
argocd app sync -l eai.io/environment=prod,eai.io/layer=data
# First install: OpenBao starts sealed. Initialise and unseal it, run deploy/openbao/bootstrap.sh,
# then write the entries of docs/operations/secrets.md.
argocd app wait -l eai.io/environment=prod,eai.io/layer=data --health --timeout 1800
for layer in platform apps; do
argocd app sync -l eai.io/environment=prod,eai.io/layer=$layer
argocd app wait -l eai.io/environment=prod,eai.io/layer=$layer --health --timeout 1800
done

The AppProject freeze window (from Friday 18:00, for 62 hours) blocks automatic prod syncs; a manual sync is still allowed.

With a central Argo CD managing several clusters, register each cluster with its environment (argocd cluster add <context> --label eai.io/environment=<env>) and add its address to destinations in deploy/argocd/appproject.yaml.