EDDI runs natively on Kubernetes. This guide covers deployment options from a simple quickstart to production-grade configurations.
- Kubernetes cluster (1.26+) — minikube, kind, GKE, EKS, AKS, or any conformant cluster
- kubectl configured to access your cluster
- Helm 3 (optional, for Helm chart deployment)
Deploy EDDI + MongoDB with one command:
kubectl apply -f https://raw.githubusercontent.com/labsai/EDDI/main/k8s/quickstart.yamlThen generate and store a vault master key:
# Generate the secret
kubectl create secret generic eddi-secrets \
--namespace=eddi \
--from-literal=EDDI_VAULT_MASTER_KEY="$(openssl rand -base64 24)" \
--dry-run=client -o yaml | kubectl apply -f -
# Restart EDDI to pick up the key
kubectl rollout restart deployment/eddi -n eddi
# Access EDDI
kubectl port-forward svc/eddi 7070:7070 -n eddiOpen http://localhost:7070.
# Clone the repo
git clone https://github.com/labsai/EDDI.git && cd EDDI
# Generate vault key + create K8s secret
bash k8s/create-secrets.sh
# Deploy with MongoDB
kubectl apply -k k8s/overlays/mongodb/PowerShell:
.\k8s\create-secrets.ps1
kubectl apply -k k8s\overlays\mongodb\helm install eddi ./helm/eddi \
--set eddi.vaultMasterKey="$(openssl rand -base64 24)" \
--namespace eddi --create-namespaceEDDI provides modular overlays (Kustomize) and Helm values for different deployment profiles:
| Backend | Kustomize | Helm |
|---|---|---|
| MongoDB (default) | kubectl apply -k k8s/overlays/mongodb/ |
--set mongodb.enabled=true |
| PostgreSQL | kubectl apply -k k8s/overlays/postgres/ |
--set postgres.enabled=true --set mongodb.enabled=false --set eddi.datastoreType=postgres |
The component overlays (auth, nats, monitoring, etc.) are designed to be composed with a database overlay. They do not include the base EDDI manifests on their own.
| Component | Description | Helm Values |
|---|---|---|
| Keycloak Auth | OIDC authentication | --set keycloak.enabled=true --set eddi.oidc.enabled=true |
| NATS JetStream | Durable messaging for multi-replica | --set nats.enabled=true --set eddi.messagingType=nats |
| Manager UI | Configuration dashboard | --set manager.enabled=true |
| Monitoring | Prometheus + Grafana | --set monitoring.prometheus.enabled=true |
| Ingress | External HTTPS access | --set ingress.enabled=true --set ingress.hosts[0].host=eddi.example.com |
| Production | HPA, PDB, NetworkPolicy | --set autoscaling.enabled=true --set podDisruptionBudget.enabled=true |
Kustomize takes one directory as input. To combine components, create a kustomization.yaml that references multiple overlays:
# my-deployment/kustomization.yaml
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
namespace: eddi
resources:
- ../k8s/overlays/mongodb # Base + MongoDB
- ../k8s/overlays/auth/keycloak-deployment.yaml # Keycloak
- ../k8s/overlays/manager/manager-deployment.yaml # Manager UI
patches:
- target: { kind: ConfigMap, name: eddi-config }
patch: |
- op: replace
path: /data/QUARKUS_OIDC_TENANT_ENABLED
value: "true"Ready-made examples are provided in k8s/examples/:
# MongoDB + Auth + Monitoring + Manager
kubectl apply -k k8s/examples/mongodb-full/
# PostgreSQL + NATS + Production hardening
kubectl apply -k k8s/examples/postgres-ha/┌──────────────────────────────────────────────┐
│ Ingress │
│ (nginx / traefik) │
└──────────────┬───────────────────────────────┘
│
┌─────────▼──────────┐
│ EDDI Service │
│ (ClusterIP:7070) │
└─────────┬──────────┘
│
┌──────────▼──────────┐ ┌─────────────┐
│ EDDI Deployment │───▶│ MongoDB │
│ (labsai/eddi:latest) │ │ StatefulSet │
│ │ └─────────────┘
│ replicas: 1-10 │ ┌─────────────┐
│ (HPA auto-scales) │───▶│ PostgreSQL │
└──────────────────────┘ │ StatefulSet │
│ └─────────────┘
┌──────────▼──────────┐
│ NATS JetStream │ (optional, for multi-replica)
│ StatefulSet │
└──────────────────────┘
The vault master key encrypts all stored API keys and secrets. If you lose this key, encrypted secrets are unrecoverable.
Three ways to manage it:
-
Helper script (recommended for initial setup):
bash k8s/create-secrets.sh
-
Manual kubectl:
kubectl create secret generic eddi-secrets \ --namespace=eddi \ --from-literal=EDDI_VAULT_MASTER_KEY="$(openssl rand -base64 24)" -
External secrets (production): Use External Secrets Operator to sync from AWS Secrets Manager, HashiCorp Vault, Azure Key Vault, etc.
EDDI runs as non-root user (UID 185) and is compatible with restricted Pod Security Standards:
securityContext:
runAsNonRoot: true
runAsUser: 185
runAsGroup: 185The production overlay includes a NetworkPolicy that restricts EDDI to:
- Ingress: HTTP port 7070 from within the namespace + Ingress controllers
- Egress: Database (MongoDB/PG), NATS, Keycloak, DNS, and external HTTPS (port 443 for LLM APIs)
Default configuration uses in-memory messaging — suitable for development and low-traffic deployments.
For horizontal scaling, enable NATS JetStream for durable message ordering:
Kustomize:
# Use the ready-made HA example
kubectl apply -k k8s/examples/postgres-ha/Helm:
helm install eddi ./helm/eddi \
--set eddi.replicas=2 \
--set nats.enabled=true \
--set eddi.messagingType=nats \
--set autoscaling.enabled=true \
--namespace eddi --create-namespaceEDDI exposes Prometheus metrics at /q/metrics. The EDDI Deployment includes Prometheus scrape annotations by default:
annotations:
prometheus.io/scrape: "true"
prometheus.io/port: "7070"
prometheus.io/path: "/q/metrics"Deploy the monitoring stack using the full example or Helm:
# Kustomize (with MongoDB + Auth + Monitoring)
kubectl apply -k k8s/examples/mongodb-full/
# Helm
helm install eddi ./helm/eddi \
--set monitoring.prometheus.enabled=true \
--set monitoring.grafana.enabled=true
# Access Grafana
kubectl port-forward svc/grafana 3000:3000 -n eddi
# Open http://localhost:3000 (admin/admin)EDDI provides three probe endpoints:
| Endpoint | Probe Type | Purpose |
|---|---|---|
/q/health/live |
Liveness | Process is alive |
/q/health/ready |
Readiness + Startup | DB connected, ready for traffic |
/q/metrics |
— | Prometheus metrics |
k8s/
├── base/ # Core EDDI manifests
├── overlays/
│ ├── mongodb/ # MongoDB backend (standalone)
│ ├── postgres/ # PostgreSQL backend (standalone)
│ ├── nats/ # NATS JetStream (component)
│ ├── auth/ # Keycloak authentication (component)
│ ├── monitoring/ # Prometheus + Grafana (component)
│ ├── manager/ # Manager UI (component)
│ ├── ingress/ # Ingress resource (component)
│ └── production/ # HPA, PDB, NetworkPolicy (component)
├── examples/
│ ├── mongodb-full/ # MongoDB + Auth + Monitoring + Manager
│ └── postgres-ha/ # PostgreSQL + NATS + Production
├── create-secrets.sh # Vault key generator (bash)
├── create-secrets.ps1 # Vault key generator (PowerShell)
└── quickstart.yaml # All-in-one manifest
helm/
└── eddi/ # Helm chart
├── Chart.yaml
├── values.yaml
└── templates/
Note: Overlays marked (standalone) include the base and can be applied directly with
kubectl apply -k. Overlays marked (component) must be composed with a standalone overlay — see Composing Kustomize Overlays.
Check if the database is reachable:
kubectl logs -n eddi deployment/eddi
kubectl get pods -n eddiCommon causes:
- MongoDB/PostgreSQL not yet ready (wait for StatefulSet pod)
- Incorrect connection string in ConfigMap
- Volume claims pending (check
kubectl get pvc -n eddi)
Check the health endpoint:
kubectl exec -n eddi deployment/eddi -- curl -s localhost:7070/q/health/readyIf you see "vault master key not set" warnings, create the secret:
bash k8s/create-secrets.sh
kubectl rollout restart deployment/eddi -n eddiIf PVCs aren't provisioning, check your StorageClass:
kubectl get sc # List available StorageClasses
kubectl get pvc -n eddi # Check PVC status
kubectl describe pvc -n eddi # See events / errorsIf your cluster doesn't have a default StorageClass, uncomment storageClassName in the StatefulSet manifests.