Cloud Provider Deployment
Deploy Dvara on AWS, Google Cloud, or Azure with provider-specific configurations and best practices.
This guide assumes you have completed the Kubernetes / Helm setup and are familiar with the Helm chart configuration.
AWS (EKS)
Prerequisites
- eksctl or an existing EKS cluster (Kubernetes 1.28+)
- AWS CLI configured with appropriate permissions
- Helm 3.x installed
Create an EKS Cluster
eksctl create cluster \
--name dvara \
--region us-east-1 \
--nodegroup-name standard \
--node-type m6i.large \
--nodes 3 \
--nodes-min 2 \
--nodes-max 5 \
--managed
Install the AWS Load Balancer Controller
Required for ALB-based Ingress:
# Install the controller (see AWS docs for full IAM setup)
helm repo add eks https://aws.github.io/eks-charts
helm install aws-load-balancer-controller eks/aws-load-balancer-controller \
-n kube-system \
--set clusterName=dvara \
--set serviceAccount.create=true
Store Secrets in AWS Secrets Manager
# Create the secret
aws secretsmanager create-secret \
--name dvara/provider-credentials \
--secret-string '{
"openai-api-key": "sk-...",
"anthropic-api-key": "sk-ant-...",
"enterprise-license-key": "eyJhbGci..."
}'
Use the External Secrets Operator to sync into Kubernetes:
# external-secret.yaml
apiVersion: external-secrets.io/v1beta1
kind: ExternalSecret
metadata:
name: dvara-provider-keys
spec:
refreshInterval: 1h
secretStoreRef:
name: aws-secrets-manager
kind: ClusterSecretStore
target:
name: dvara-external
data:
- secretKey: openai-api-key
remoteRef:
key: dvara/provider-credentials
property: openai-api-key
- secretKey: anthropic-api-key
remoteRef:
key: dvara/provider-credentials
property: anthropic-api-key
- secretKey: enterprise-license-key
remoteRef:
key: dvara/provider-credentials
property: enterprise-license-key
kubectl apply -f external-secret.yaml
Deploy with ALB Ingress + IRSA
# aws-values.yaml
gatewayServer:
replicaCount: 3
javaOpts: "-Xms512m -Xmx768m"
serviceAccount:
annotations:
eks.amazonaws.com/role-arn: arn:aws:iam::123456789012:role/dvara-gateway
autoscaling:
enabled: true
minReplicas: 3
maxReplicas: 10
targetCPUUtilizationPercentage: 70
pdb:
enabled: true
minAvailable: 2
serviceMonitor:
enabled: true
additionalLabels:
release: kube-prometheus-stack
topologySpreadConstraints:
- maxSkew: 1
topologyKey: topology.kubernetes.io/zone
whenUnsatisfiable: DoNotSchedule
labelSelector:
matchLabels:
app.kubernetes.io/component: gateway-server
gatewayUi:
enabled: true
secrets:
create: false
existingSecret: dvara-external
ingress:
enabled: true
className: alb
annotations:
alb.ingress.kubernetes.io/scheme: internet-facing
alb.ingress.kubernetes.io/target-type: ip
alb.ingress.kubernetes.io/listen-ports: '[{"HTTPS":443}]'
alb.ingress.kubernetes.io/certificate-arn: arn:aws:acm:us-east-1:123456789012:certificate/abc-123
alb.ingress.kubernetes.io/ssl-policy: ELBSecurityPolicy-TLS13-1-2-2021-06
alb.ingress.kubernetes.io/healthcheck-path: /actuator/health/readiness
gatewayServer:
hosts:
- host: gateway.mycompany.com
paths:
- path: /
pathType: Prefix
gatewayUi:
hosts:
- host: admin.mycompany.com
paths:
- path: /
pathType: Prefix
helm install dvara charts/dvara/ -f aws-values.yaml
AWS Bedrock with IRSA (No API Keys)
Use IAM Roles for Service Accounts to access Bedrock without static credentials:
# Create the IAM policy
cat > bedrock-policy.json << 'EOF'
{
"Version": "2012-10-17",
"Statement": [{
"Effect": "Allow",
"Action": [
"bedrock:InvokeModel",
"bedrock:InvokeModelWithResponseStream"
],
"Resource": "arn:aws:bedrock:*:*:model/*"
}]
}
EOF
aws iam create-policy \
--policy-name DvaraBedrockAccess \
--policy-document file://bedrock-policy.json
# Associate with the service account
eksctl create iamserviceaccount \
--name dvara-server \
--namespace default \
--cluster dvara \
--attach-policy-arn arn:aws:iam::123456789012:policy/DvaraBedrockAccess \
--approve
# bedrock-values.yaml
gatewayServer:
serviceAccount:
create: false
name: dvara-server
secrets:
bedrockEnabled: "true"
helm install dvara charts/dvara/ -f bedrock-values.yaml
Database and Cache for Enterprise Persistence
Enterprise deployments can use PostgreSQL and Redis for durable storage instead of in-memory defaults.
RDS PostgreSQL:
aws rds create-db-instance \
--db-instance-identifier dvara-db \
--db-instance-class db.r6g.large \
--engine postgres --engine-version 14 \
--master-username dvara --master-user-password "${DB_PASSWORD}" \
--allocated-storage 100 --storage-encrypted \
--vpc-security-group-ids sg-... --db-subnet-group-name dvara-subnet
ElastiCache Redis:
aws elasticache create-replication-group \
--replication-group-id dvara-redis \
--replication-group-description "Dvara rate-limit and cache" \
--engine redis --engine-version 7.0 \
--cache-node-type cache.r6g.large \
--num-cache-clusters 2 --automatic-failover-enabled \
--transit-encryption-enabled --auth-token "${REDIS_PASSWORD}"
Helm values for database and cache:
gatewayServer:
env:
- name: SPRING_DATASOURCE_URL
value: "jdbc:postgresql://dvara-db.xxx.us-east-1.rds.amazonaws.com:5432/dvara"
- name: SPRING_DATASOURCE_USERNAME
value: "dvara"
- name: SPRING_DATASOURCE_PASSWORD
valueFrom:
secretKeyRef:
name: dvara-db-credentials
key: password
- name: SPRING_DATA_REDIS_HOST
value: "dvara-redis.xxx.use1.cache.amazonaws.com"
- name: SPRING_DATA_REDIS_PASSWORD
valueFrom:
secretKeyRef:
name: dvara-redis-credentials
key: password
- name: SPRING_DATA_REDIS_SSL_ENABLED
value: "true"
Multi-Region on AWS
Primary region (us-east-1):
# primary-values.yaml
gatewayServer:
replicaCount: 3
gatewayMode: full
region:
id: us-east-1
name: US East
autoscaling:
enabled: true
minReplicas: 3
maxReplicas: 10
gatewayUi:
enabled: true
secrets:
providerKeys:
openai: sk-...
gatewayInternalSecret: "shared-internal-secret-change-me"
enterpriseLicenseKey: "eyJhbGci..."
Secondary region (eu-west-1) — syncs config from the primary:
# secondary-eu-values.yaml
gatewayServer:
replicaCount: 2
gatewayMode: full
region:
id: eu-west-1
name: EU West
autoscaling:
enabled: true
minReplicas: 2
maxReplicas: 10
env:
- name: GATEWAY_CONFIG_SYNC_ENABLED
value: "true"
- name: GATEWAY_CONFIG_SYNC_CONTROL_PLANE_URL
value: "http://dvara-server.us-east-1.internal:8080"
- name: GATEWAY_CONFIG_SYNC_INTERNAL_SECRET
value: "shared-internal-secret-change-me"
gatewayUi:
enabled: false
secrets:
providerKeys:
openai: sk-...
enterpriseLicenseKey: "eyJhbGci..."
# Deploy primary in us-east-1
kubectl config use-context us-east-1
helm install dvara charts/dvara/ -f primary-values.yaml
# Deploy secondary in eu-west-1
kubectl config use-context eu-west-1
helm install dvara-eu charts/dvara/ -f secondary-eu-values.yaml
Google Cloud (GKE)
Prerequisites
- gcloud CLI configured
- An existing GKE cluster or permissions to create one
- Helm 3.x installed
Create a GKE Cluster
gcloud container clusters create dvara \
--region us-central1 \
--num-nodes 3 \
--machine-type e2-standard-4 \
--enable-autoscaling --min-nodes 2 --max-nodes 6 \
--workload-pool=my-project.svc.id.goog
gcloud container clusters get-credentials dvara --region us-central1
Store Secrets in Google Secret Manager
# Create secrets
echo -n "sk-..." | gcloud secrets create dvara-openai-key --data-file=-
echo -n "sk-ant-..." | gcloud secrets create dvara-anthropic-key --data-file=-
echo -n "eyJhbGci..." | gcloud secrets create dvara-license-key --data-file=-
Sync with External Secrets Operator:
# gcp-external-secret.yaml
apiVersion: external-secrets.io/v1beta1
kind: ExternalSecret
metadata:
name: dvara-provider-keys
spec:
refreshInterval: 1h
secretStoreRef:
name: gcp-secret-manager
kind: ClusterSecretStore
target:
name: dvara-external
data:
- secretKey: openai-api-key
remoteRef:
key: dvara-openai-key
- secretKey: anthropic-api-key
remoteRef:
key: dvara-anthropic-key
- secretKey: enterprise-license-key
remoteRef:
key: dvara-license-key
Deploy with GCE Ingress + Gemini
# gcp-values.yaml
gatewayServer:
replicaCount: 3
javaOpts: "-Xms512m -Xmx768m"
serviceAccount:
annotations:
iam.gke.io/gcp-service-account: dvara@my-project.iam.gserviceaccount.com
autoscaling:
enabled: true
minReplicas: 3
maxReplicas: 10
pdb:
enabled: true
minAvailable: 2
serviceMonitor:
enabled: true
topologySpreadConstraints:
- maxSkew: 1
topologyKey: topology.kubernetes.io/zone
whenUnsatisfiable: DoNotSchedule
labelSelector:
matchLabels:
app.kubernetes.io/component: gateway-server
gatewayUi:
enabled: true
secrets:
create: false
existingSecret: dvara-external
ingress:
enabled: true
className: gce
annotations:
kubernetes.io/ingress.global-static-ip-name: dvara-ip
networking.gke.io/managed-certificates: dvara-cert
gatewayServer:
hosts:
- host: gateway.mycompany.com
paths:
- path: /
pathType: Prefix
gatewayUi:
hosts:
- host: admin.mycompany.com
paths:
- path: /
pathType: Prefix
For Google Managed Certificates:
# managed-cert.yaml
apiVersion: networking.gke.io/v1
kind: ManagedCertificate
metadata:
name: dvara-cert
spec:
domains:
- gateway.mycompany.com
- admin.mycompany.com
kubectl apply -f managed-cert.yaml
helm install dvara charts/dvara/ -f gcp-values.yaml
Database and Cache for Enterprise Persistence
Cloud SQL PostgreSQL:
gcloud sql instances create dvara-db \
--database-version=POSTGRES_14 --tier=db-custom-2-8192 \
--region=us-central1 --storage-size=100GB --storage-auto-increase \
--require-ssl --availability-type=REGIONAL
gcloud sql databases create dvara --instance=dvara-db
gcloud sql users create dvara --instance=dvara-db --password="${DB_PASSWORD}"
Memorystore Redis:
gcloud redis instances create dvara-redis \
--size=5 --region=us-central1 --redis-version=redis_7_0 \
--tier=STANDARD_HA --transit-encryption-mode=SERVER_AUTHENTICATION
Helm values: use the same SPRING_DATASOURCE_* and SPRING_DATA_REDIS_* environment variables shown in the AWS section, substituting Cloud SQL and Memorystore endpoints.
Gemini Provider Setup
# With Gemini API key
secrets:
providerKeys:
gemini: AIzaSy...
# Or via Workload Identity (for Vertex AI)
gatewayServer:
serviceAccount:
annotations:
iam.gke.io/gcp-service-account: dvara@my-project.iam.gserviceaccount.com
Grant the service account access to Vertex AI:
gcloud projects add-iam-policy-binding my-project \
--member="serviceAccount:dvara@my-project.iam.gserviceaccount.com" \
--role="roles/aiplatform.user"
Azure (AKS)
Prerequisites
- Azure CLI configured
- An existing AKS cluster or permissions to create one
- Helm 3.x installed
Create an AKS Cluster
az group create --name dvara-rg --location eastus
az aks create \
--resource-group dvara-rg \
--name dvara \
--node-count 3 \
--node-vm-size Standard_D4s_v3 \
--enable-cluster-autoscaler \
--min-count 2 \
--max-count 6 \
--enable-managed-identity \
--enable-workload-identity \
--enable-oidc-issuer
az aks get-credentials --resource-group dvara-rg --name dvara
Store Secrets in Azure Key Vault
# Create key vault
az keyvault create --name dvara-vault --resource-group dvara-rg --location eastus
# Store secrets
az keyvault secret set --vault-name dvara-vault --name openai-api-key --value "sk-..."
az keyvault secret set --vault-name dvara-vault --name anthropic-api-key --value "sk-ant-..."
az keyvault secret set --vault-name dvara-vault --name enterprise-license-key --value "eyJhbGci..."
Use the Azure Key Vault Provider for Secrets Store CSI Driver:
# secret-provider-class.yaml
apiVersion: secrets-store.csi.x-k8s.io/v1
kind: SecretProviderClass
metadata:
name: dvara-keyvault
spec:
provider: azure
parameters:
usePodIdentity: "false"
useVMManagedIdentity: "true"
userAssignedIdentityID: "<managed-identity-client-id>"
keyvaultName: dvara-vault
tenantId: "<azure-tenant-id>"
objects: |
array:
- |
objectName: openai-api-key
objectType: secret
- |
objectName: anthropic-api-key
objectType: secret
- |
objectName: enterprise-license-key
objectType: secret
secretObjects:
- secretName: dvara-external
type: Opaque
data:
- objectName: openai-api-key
key: openai-api-key
- objectName: anthropic-api-key
key: anthropic-api-key
- objectName: enterprise-license-key
key: enterprise-license-key
kubectl apply -f secret-provider-class.yaml
Deploy with NGINX Ingress Controller
# Install NGINX Ingress Controller
helm repo add ingress-nginx https://kubernetes.github.io/ingress-nginx
helm install ingress-nginx ingress-nginx/ingress-nginx \
--namespace ingress-nginx --create-namespace \
--set controller.service.annotations."service\.beta\.kubernetes\.io/azure-load-balancer-health-probe-request-path"=/healthz
# azure-values.yaml
gatewayServer:
replicaCount: 3
javaOpts: "-Xms512m -Xmx768m"
autoscaling:
enabled: true
minReplicas: 3
maxReplicas: 10
pdb:
enabled: true
minAvailable: 2
serviceMonitor:
enabled: true
topologySpreadConstraints:
- maxSkew: 1
topologyKey: topology.kubernetes.io/zone
whenUnsatisfiable: DoNotSchedule
labelSelector:
matchLabels:
app.kubernetes.io/component: gateway-server
gatewayUi:
enabled: true
secrets:
create: false
existingSecret: dvara-external
ingress:
enabled: true
className: nginx
annotations:
cert-manager.io/cluster-issuer: letsencrypt-prod
nginx.ingress.kubernetes.io/ssl-redirect: "true"
nginx.ingress.kubernetes.io/proxy-body-size: "10m"
gatewayServer:
hosts:
- host: gateway.mycompany.com
paths:
- path: /
pathType: Prefix
tls:
- secretName: gateway-tls
hosts:
- gateway.mycompany.com
gatewayUi:
hosts:
- host: admin.mycompany.com
paths:
- path: /
pathType: Prefix
tls:
- secretName: admin-tls
hosts:
- admin.mycompany.com
helm install dvara charts/dvara/ -f azure-values.yaml
Database and Cache for Enterprise Persistence
Azure Database for PostgreSQL:
az postgres flexible-server create \
--resource-group dvara-rg --name dvara-db \
--version 14 --sku-name Standard_D2s_v3 --storage-size 128 \
--admin-user dvara --admin-password "${DB_PASSWORD}" \
--tier GeneralPurpose --high-availability ZoneRedundant
Azure Cache for Redis:
az redis create \
--resource-group dvara-rg --name dvara-redis \
--sku Premium --vm-size P1 --minimum-tls-version 1.2 \
--enable-non-ssl-port false
Helm values: use the same SPRING_DATASOURCE_* and SPRING_DATA_REDIS_* environment variables shown in the AWS section, substituting Azure Database for PostgreSQL and Azure Cache for Redis endpoints.
Azure OpenAI Service
To use Azure OpenAI instead of the public OpenAI API, override the base URL:
# azure-openai-values.yaml
gatewayServer:
env:
- name: OPENAI_BASE_URL
value: "https://my-resource.openai.azure.com/openai/deployments/gpt-4o"
secrets:
providerKeys:
openai: "<azure-openai-api-key>"
Monitoring Stack
All cloud providers can use the same monitoring setup. Install the kube-prometheus-stack for Prometheus + Grafana:
helm repo add prometheus-community https://prometheus-community.github.io/helm-charts
helm install monitoring prometheus-community/kube-prometheus-stack \
--namespace monitoring --create-namespace \
--set grafana.adminPassword=admin
Enable ServiceMonitor in Dvara:
gatewayServer:
serviceMonitor:
enabled: true
interval: 15s
additionalLabels:
release: monitoring
mcpProxyServer:
serviceMonitor:
enabled: true
interval: 15s
additionalLabels:
release: monitoring
Import the included Grafana dashboard from grafana/dashboards/ for pre-built panels covering request rates, latencies, token usage, provider health, cost metrics, and agent session stats.
Key Prometheus Metrics to Alert On
# Example alerting rules
groups:
- name: dvara
rules:
- alert: HighErrorRate
expr: rate(gateway_requests_total{status=~"5.."}[5m]) / rate(gateway_requests_total[5m]) > 0.05
for: 5m
labels:
severity: critical
- alert: HighLatency
expr: histogram_quantile(0.95, rate(gateway_latency_seconds_bucket[5m])) > 5
for: 5m
labels:
severity: warning
- alert: BudgetCapBreached
expr: increase(gateway_budget_blocked_total[1h]) > 0
labels:
severity: warning
Enterprise Features on Cloud
Vault Integration
Dvara supports HashiCorp Vault, AWS Secrets Manager, and Azure Key Vault as credential backends for LLM provider keys. This is separate from Kubernetes secret management — it provides runtime credential resolution for the gateway itself.
HashiCorp Vault (any cloud):
gatewayServer:
env:
- name: GATEWAY_VAULT_BACKEND
value: hashicorp
- name: VAULT_ADDR
value: "https://vault.internal:8200"
- name: VAULT_AUTH_METHOD
value: approle
- name: VAULT_ROLE_ID
valueFrom:
secretKeyRef:
name: vault-credentials
key: role-id
- name: VAULT_SECRET_ID
valueFrom:
secretKeyRef:
name: vault-credentials
key: secret-id
AWS Secrets Manager:
gatewayServer:
env:
- name: GATEWAY_VAULT_BACKEND
value: aws-secrets-manager
- name: AWS_VAULT_REGION
value: us-east-1
- name: AWS_SECRET_NAME
value: dvara/provider-credentials
serviceAccount:
annotations:
eks.amazonaws.com/role-arn: arn:aws:iam::123456789012:role/dvara-secrets
Azure Key Vault:
gatewayServer:
env:
- name: GATEWAY_VAULT_BACKEND
value: azure-key-vault
- name: AZURE_VAULT_URL
value: "https://dvara-vault.vault.azure.net"
- name: AZURE_CLIENT_ID
value: "<managed-identity-client-id>"
OIDC / SSO Setup
Enable OIDC-based authentication for the admin API:
gatewayServer:
env:
- name: GATEWAY_SECURITY_ENABLED
value: "true"
- name: GATEWAY_OIDC_ISSUER_URI
value: "https://login.microsoftonline.com/<tenant-id>/v2.0" # Azure AD
# value: "https://accounts.google.com" # Google
# value: "https://cognito-idp.us-east-1.amazonaws.com/<pool>" # AWS Cognito
- name: GATEWAY_OIDC_AUDIENCE
value: "dvara-gateway"
- name: GATEWAY_OIDC_ROLE_CLAIM
value: "roles" # or "realm_access.roles" for Keycloak
Database and Cache Requirements
PostgreSQL and Redis are optional -- standalone and OSS-only deployments use in-memory defaults with no external dependencies. Enterprise deployments that enable persistent audit trails, rate limiting, or semantic caching should provision both.
| Dependency | Minimum Version | Purpose |
|---|---|---|
| PostgreSQL | 14+ | Audit events, policies, cost records, token usage, tenants, API keys |
| Redis | 6+ | Rate limiting (Bucket4j), semantic cache, session tracking |
Flyway migrations run automatically on first startup when a datasource is configured. No manual schema setup is required. Subsequent application upgrades apply incremental migrations without data loss.
Connection pool defaults: HikariCP with maximumPoolSize=10. For high-throughput deployments, increase via SPRING_DATASOURCE_HIKARI_MAXIMUM_POOL_SIZE.
Quick Reference: Cloud-Specific Values
| Setting | AWS | GCP | Azure |
|---|---|---|---|
| Ingress class | alb | gce | nginx |
| TLS termination | ACM certificate ARN annotation | GKE ManagedCertificate | cert-manager + Let's Encrypt |
| Service account identity | IRSA (eks.amazonaws.com/role-arn) | Workload Identity (iam.gke.io/gcp-service-account) | Managed Identity |
| Secret management | Secrets Manager + External Secrets | Secret Manager + External Secrets | Key Vault + CSI Driver |
| Node type (recommended) | m6i.large (2 vCPU, 8 GB) | e2-standard-4 (4 vCPU, 16 GB) | Standard_D4s_v3 (4 vCPU, 16 GB) |
| Autoscaler | Cluster Autoscaler or Karpenter | GKE Autopilot or Cluster Autoscaler | AKS Cluster Autoscaler |
| Managed PostgreSQL | RDS for PostgreSQL | Cloud SQL for PostgreSQL | Azure Database for PostgreSQL |
| Managed Redis | ElastiCache for Redis | Memorystore for Redis | Azure Cache for Redis |
Production Checklist
Before going to production on any cloud provider:
- Enable HPA with appropriate min/max replicas
- Enable PDB with
minAvailable >= 2 - Configure topology spread constraints for cross-zone scheduling
- Use external secret management (not Helm
--setfor API keys) - Enable Ingress with TLS termination
- Enable ServiceMonitor for Prometheus scraping
- Set JVM options:
-Xms512m -Xmx768m(adjust for workload) - Configure
terminationGracePeriodSeconds: 45andpreStopSleepSeconds: 5 - Set
maxSurge: 1andmaxUnavailable: 0for zero-downtime deploys - Provision PostgreSQL 14+ and Redis 6+ for enterprise persistence (optional for standalone mode)
- Enable enterprise license key for governance features
- Set up alerting on error rate, latency, and budget metrics
- Configure OIDC/SSO for admin API access (enterprise)
- Enable audit HMAC signing with a production secret
- Review and set budget caps for cost governance