Deployment, Helm & Secrets
Eloquent is distributed as a Helm chart and can be deployed to any Kubernetes cluster with a single command.
Prerequisites
Before deploying, ensure your cluster has:
| Requirement | Version | Notes |
|---|---|---|
| Kubernetes | 1.20+ | AKS, GKE, OKE, or any conformant cluster |
| Helm | 3.x | For chart installation |
| ingress-nginx | Latest | Required for HTTP routing |
| cert-manager | Latest | Required only if using platform-managed TLS |
| GHCR credentials | — | GitHub Container Registry access for pulling images |
Install ingress-nginx
helm repo add ingress-nginx https://kubernetes.github.io/ingress-nginx
helm install ingress-nginx ingress-nginx/ingress-nginx \
--set controller.service.annotations."service\.beta\.kubernetes\.io/azure-load-balancer-health-probe-request-path"=/healthz
Install cert-manager (optional — for automatic TLS)
helm repo add jetstack https://charts.jetstack.io
helm install cert-manager jetstack/cert-manager --set crds.enabled=true
Create namespace and registry secret
kubectl create namespace eloquent
kubectl create secret docker-registry ghcr-credentials \
-n eloquent \
--docker-server=ghcr.io \
--docker-username=<github-username> \
--docker-password=<github-pat-with-read-packages>
One-Command Deploy
helm upgrade --install eloquent \
oci://ghcr.io/blazi-commerce/eloquent/charts/eloquent \
-n eloquent \
-f eloquent-values.yaml \
--set image.tag=prod
This deploys all frontend apps, backend services, and infrastructure components as configured in your values file.
Values File Structure
Configuration is layered — each level overrides the previous:
values.yaml # Chart defaults (all services, dev resources)
└── eloquent-values.yaml # Customer-specific overrides
└── eloquent-secrets.yaml # Secrets (gitignored, never committed)
You only need to override what differs from the defaults.
Core Configuration
Customer Deployment
customerDeployment:
enabled: true
domain: "customer.example.com"
apiGatewayPublicUrl: "https://api.customer.example.com"
chatPublicUrl: "https://chat.customer.example.com/v1/chat"
sandboxSubdomain: "sandbox"
appBaseUrl: "https://app.customer.example.com"
Frontend Applications
Enable or disable each frontend app and set its hostname:
frontend:
eloquentApp:
enabled: true
host: app.customer.example.com
adminApp:
enabled: true
host: admin.customer.example.com
Ingress
Two modes are supported:
Platform-managed (cert-manager handles TLS):
ingress:
enabled: true
className: nginx
annotations:
cert-manager.io/cluster-issuer: letsencrypt-prod
nginx.ingress.kubernetes.io/proxy-body-size: "100m"
Customer-managed (your own nginx/reverse proxy handles TLS):
ingress:
enabled: false
When ingress is disabled, your own reverse proxy routes traffic to Kubernetes services via internal DNS.
Secrets
All secrets are configured under the secrets key. Keep these in a separate file that is never committed to version control.
Required Secrets
secrets:
# JWT — must be identical across all services
jwtSecret: "<random-256-bit-secret>"
# Database credentials
postgresPassword: "<strong-password>"
redisPassword: "<strong-password>"
clickhousePassword: "<strong-password>"
# NextAuth session encryption
authSecret: "<random-secret>"
# OAuth (Google SSO)
authGoogleId: "<google-oauth-client-id>"
authGoogleSecret: "<google-oauth-client-secret>"
LLM Providers
At least one LLM provider is required for AI agents:
secrets:
# Azure OpenAI (primary)
azureOpenaiApiKey: "<key>"
azureOpenaiEndpoint: "https://<resource>.openai.azure.com"
azureOpenaiResourceName: "<resource-name>"
azureOpenaiApiVersion: "2024-12-01-preview"
azureOpenaiMiniModel: "gpt-4o-mini"
azureOpenaiNanoModel: "gpt-4o-mini"
# Anthropic (optional)
anthropicApiKey: "<key>"
anthropicApiUrl: "https://<endpoint>"
anthropicModelMap: "<model-mapping>"
Cloud Storage
File uploads require a storage backend:
secrets:
# Azure Blob Storage
azureStorageAccountName: "<account>"
azureStorageAccountKey: "<key>"
azureStorageConnectionString: "<connection-string>"
azureStorageContainerName: "files"
# OR S3-compatible storage
s3AccessKeyId: "<key>"
s3SecretAccessKey: "<secret>"
s3Bucket: "<bucket>"
s3Region: "<region>"
s3Endpoint: "<endpoint-url>"
Document Intelligence
For PDF processing and document analysis:
secrets:
azureDocIntelligenceKey: "<key>"
azureDocIntelligenceEndpoint: "https://<resource>.cognitiveservices.azure.com"
For sending invite emails and notifications:
secrets:
# Azure Communication Services (primary)
AZURE_EMAIL_ENDPOINT: "https://<resource>.communication.azure.com"
AZURE_EMAIL_ACCESS_KEY: "<key>"
AZURE_EMAIL_SENDER: "DoNotReply@yourdomain.com"
# OR SMTP fallback
smtp:
host: "smtp.example.com"
port: "587"
user: "<username>"
pass: "<password>"
fromEmail: "noreply@example.com"
Integrations (Optional)
secrets:
# Google Calendar (booking system)
googleCalendar:
serviceAccountEmail: "<email>"
serviceAccountPrivateKey: "<key>"
calendarId: "<calendar-id>"
timezone: "Asia/Dubai"
# WhatsApp Business API
whatsapp:
products: "eloquent"
eloquent:
enabled: true
phoneNumberId: "<id>"
accessToken: "<token>"
verifyToken: "<token>"
appSecret: "<secret>"
# Google AI (image generation)
googleApiKey: "<key>"
Infrastructure Components
Each infrastructure component can be Helm-managed or external:
# Helm-managed (default)
postgresql:
enabled: true
persistence:
size: 10Gi
clickhouse:
enabled: true
persistence:
size: 20Gi
redis:
enabled: true
persistence:
size: 5Gi
nats:
enabled: true # Always internal, cannot use external
To use external databases, disable the component and provide connection strings in the service environment variables.
External Database Access
For backup tools or analytics access, expose databases via internal LoadBalancer:
postgresql:
externalAccess:
enabled: true
type: LoadBalancer
annotations:
service.beta.kubernetes.io/azure-load-balancer-internal: "true"
clickhouse:
externalAccess:
enabled: true
annotations:
service.beta.kubernetes.io/azure-load-balancer-internal: "true"
Per-Service Configuration
Each service can be individually configured:
backend:
agentsService:
enabled: true
replicas: 2
resources:
requests:
cpu: 100m
memory: 256Mi
limits:
cpu: 500m
memory: 512Mi
Initial Organization Seeding
After deployment, seed the first organization and admin user:
kubectl exec -n eloquent deployment/admin-service -- \
curl -X POST http://localhost/internal/seed \
-H "Content-Type: application/json" \
-d '{
"org": {
"title": "My Organization",
"mainDomain": "example.com",
"product": "eloquent"
},
"admin": {
"email": "admin@example.com",
"firstName": "Admin",
"lastName": "User"
}
}'
The seed endpoint creates the organization, admin user, and provisions all artifacts (databases, entities, agents, skills, storage) — 15 artifacts total.
Upgrading
To upgrade to a new version:
helm upgrade eloquent \
oci://ghcr.io/blazi-commerce/eloquent/charts/eloquent \
-n eloquent \
-f eloquent-values.yaml \
--set image.tag=<new-version>
To roll back a deployment:
kubectl rollout undo deployment/<service-name> -n eloquent
Real-World Example
A production deployment on Azure AKS with customer-managed nginx:
customerDeployment:
enabled: true
domain: "customer.gov.qa"
apiGatewayPublicUrl: "https://ai-api.customer.gov.qa"
chatPublicUrl: "https://ai-chat.customer.gov.qa/v1/chat"
sandboxSubdomain: "ai-sandbox"
appBaseUrl: "https://ai-hub.customer.gov.qa"
ingress:
enabled: false # Customer nginx handles routing
frontend:
eloquentApp:
enabled: true
host: ai-hub.customer.gov.qa
adminApp:
enabled: true
host: ai-admin.customer.gov.qa
postgresql:
externalAccess:
enabled: true
annotations:
service.beta.kubernetes.io/azure-load-balancer-internal: "true"
Services are accessed from the customer namespace via Kubernetes internal DNS (e.g., api-gateway-service.eloquent.svc.cluster.local).