Skip to main content
Version: Next

Kubernetes Deployment

Version: 0.1.0

Deploy DecisionBox on any Kubernetes cluster using Helm charts.

Prerequisites

  • Kubernetes cluster (GKE, EKS, AKS, self-managed, or any CNCF-conformant cluster)
  • Helm 3.7+
  • kubectl configured for your cluster
  • MongoDB instance (Atlas, self-hosted, or the bundled Bitnami subchart)

Architecture

Ingress
└── Dashboard (Next.js, port 3000)
└── proxies /api/* to API

API Service (Go, port 8080, ClusterIP)
├── spawns Agent as K8s Jobs
├── connects to MongoDB
├── connects to Qdrant (optional, for vector search)
├── manages secrets (AES-256 or cloud provider)
└── reads domain pack prompts from /app/domain-packs/

Agent Jobs (Go, spawned per discovery run)
├── connects to MongoDB
├── connects to Qdrant (optional, for indexing)
├── calls LLM provider
└── queries data warehouse

MongoDB (standalone or Atlas)
Qdrant (optional, for vector search)

The API is internal only (ClusterIP) — never exposed to the internet. The dashboard is the only public-facing service and proxies all API requests server-side.

Quick Start

# Add the DecisionBox Helm repository
helm repo add decisionbox https://decisionbox-io.github.io/decisionbox-platform
helm repo update

# Create namespace
kubectl create namespace decisionbox

# Create API secrets (encryption key + MongoDB URI if using external MongoDB)
kubectl create secret generic decisionbox-api-secrets \
--from-literal=SECRET_ENCRYPTION_KEY="$(openssl rand -base64 32)" \
-n decisionbox

# Deploy API (with bundled MongoDB for quick start)
helm upgrade --install decisionbox-api decisionbox/decisionbox-api \
--set "extraEnvFrom[0].secretRef.name=decisionbox-api-secrets" \
-n decisionbox

# Deploy Dashboard
helm upgrade --install decisionbox-dashboard decisionbox/decisionbox-dashboard \
-n decisionbox

# Verify
kubectl get pods -n decisionbox
helm test decisionbox-api -n decisionbox

The dashboard is accessible via the ingress (enabled by default). Check the external IP:

kubectl get ingress -n decisionbox

Charts

DecisionBox charts are published to a public Helm repository. Source code is in helm-charts/.

helm repo add decisionbox https://decisionbox-io.github.io/decisionbox-platform
helm repo update
ChartDescriptionDefault PortIngress
decisionbox-apiAPI service + optional MongoDB subchart8080Disabled (internal)
decisionbox-dashboardWeb dashboard3000Enabled

Injecting Secrets with extraEnvFrom

Sensitive values (MongoDB URI, encryption keys, API keys) should never be set via --set env.KEY=VALUE — that exposes them in shell history, helm get values output, and the Deployment spec.

Instead, store secrets in a K8s Secret and reference it with extraEnvFrom:

# Create a K8s Secret with all sensitive values
kubectl create secret generic decisionbox-api-secrets \
--from-literal=SECRET_ENCRYPTION_KEY="$(openssl rand -base64 32)" \
--from-literal=MONGODB_URI="mongodb+srv://user:pass@cluster.mongodb.net/decisionbox" \
-n decisionbox

# Reference it in Helm
helm upgrade --install decisionbox-api decisionbox/decisionbox-api \
--set "extraEnvFrom[0].secretRef.name=decisionbox-api-secrets" \
-n decisionbox

The extraEnvFrom mechanism injects all keys from the K8s Secret as environment variables into the API pod. Use it for any value you don't want in version control or Helm output.

Configuration

Disable the bundled MongoDB subchart and provide your connection string via a K8s Secret:

# Create secret with MongoDB URI
kubectl create secret generic decisionbox-api-secrets \
--from-literal=SECRET_ENCRYPTION_KEY="$(openssl rand -base64 32)" \
--from-literal=MONGODB_URI="mongodb+srv://user:pass@cluster.mongodb.net/decisionbox?retryWrites=true" \
-n decisionbox

# Deploy with external MongoDB
helm upgrade --install decisionbox-api decisionbox/decisionbox-api \
--set mongodb.enabled=false \
--set env.MONGODB_DB=decisionbox \
--set "extraEnvFrom[0].secretRef.name=decisionbox-api-secrets" \
-n decisionbox

Secret Provider

By default, secrets are encrypted with AES-256 and stored in MongoDB. For production, use a cloud secret provider:

GCP Secret Manager (with Workload Identity):

helm upgrade --install decisionbox-api decisionbox/decisionbox-api \
--set env.SECRET_PROVIDER=gcp \
--set env.SECRET_GCP_PROJECT_ID=my-gcp-project \
--set env.SECRET_NAMESPACE=decisionbox \
--set "serviceAccountAnnotations.iam\.gke\.io/gcp-service-account=decisionbox-prod-api@my-gcp-project.iam.gserviceaccount.com" \
--set "extraEnvFrom[0].secretRef.name=decisionbox-api-secrets" \
-n decisionbox

The serviceAccountAnnotations binds the K8s service account to a GCP service account via Workload Identity. See Terraform GCP for automated Workload Identity setup.

AWS Secrets Manager (with IRSA or EKS Pod Identity):

helm upgrade --install decisionbox-api decisionbox/decisionbox-api \
--set env.SECRET_PROVIDER=aws \
--set env.SECRET_AWS_REGION=us-east-1 \
--set env.SECRET_NAMESPACE=decisionbox \
--set "serviceAccountAnnotations.eks\.amazonaws\.com/role-arn=arn:aws:iam::123456789012:role/decisionbox-api" \
--set "extraEnvFrom[0].secretRef.name=decisionbox-api-secrets" \
-n decisionbox

The serviceAccountAnnotations binds the K8s service account to an IAM role via IRSA or EKS Pod Identity.

Azure Key Vault (with Workload Identity):

helm upgrade --install decisionbox-api decisionbox/decisionbox-api \
--set env.SECRET_PROVIDER=azure \
--set env.SECRET_AZURE_VAULT_URL=https://my-vault.vault.azure.net/ \
--set env.SECRET_NAMESPACE=decisionbox \
--set "serviceAccountAnnotations.azure\.workload\.identity/client-id=<api-managed-identity-client-id>" \
--set "serviceAccountLabels.azure\.workload\.identity/use=true" \
--set "podLabels.azure\.workload\.identity/use=true" \
--set "extraEnvFrom[0].secretRef.name=decisionbox-api-secrets" \
-n decisionbox

The serviceAccountAnnotations binds the K8s service account to a managed identity via Azure Workload Identity. The podLabels enables the Workload Identity webhook to inject tokens. The managed identity must have the Key Vault Secrets Officer RBAC role on the vault. See Terraform Azure for automated setup.

Agent Configuration

The API spawns agent processes as K8s Jobs. Configure via Helm values:

env:
RUNNER_MODE: "kubernetes"
AGENT_IMAGE: "ghcr.io/decisionbox-io/decisionbox-agent:latest"
AGENT_NAMESPACE: "decisionbox"
AGENT_JOB_TIMEOUT_HOURS: "6"

The chart includes RBAC rules that grant the API service account permission to create and manage Jobs in its namespace.

Vector Search (Qdrant)

DecisionBox uses Qdrant for semantic search and discovery. To enable it:

helm upgrade --install decisionbox-api decisionbox/decisionbox-api \
--set qdrant.enabled=true \
--set qdrant.url="qdrant-service:6334" \
--set qdrant.apiKey="optional-secret-key" \
-n decisionbox

If qdrant.apiKey is provided, the chart automatically creates a K8s Secret and injects it as QDRANT_API_KEY. For production, it's recommended to leave qdrant.apiKey empty and instead store it in your extraEnvFrom secret.

Ingress

Dashboard (enabled by default):

# helm-charts/decisionbox-dashboard/values.yaml
ingress:
enabled: true
host: "dashboard.example.com" # optional: host-based routing
tlsSecretName: "dashboard-tls" # optional: TLS
pathType: Prefix
path: /

API (disabled by default — keep it internal):

# The API should remain ClusterIP. Do not enable API ingress.
ingress:
enabled: false

Dashboard API URL

The dashboard proxies /api/* requests to the API service. The default API_URL is http://decisionbox-api-service:8080, which assumes the API release name is decisionbox-api. If you use a different release name, update the dashboard's env.API_URL:

helm upgrade --install my-dashboard decisionbox/decisionbox-dashboard \
--set env.API_URL="http://my-custom-api-service:8080" \
-n decisionbox

Using a Values File

For repeatable deployments, create a values override file:

# values-prod.yaml (for decisionbox-api)

mongodb:
enabled: false

env:
MONGODB_DB: "decisionbox_prod"
SECRET_PROVIDER: "gcp"
SECRET_GCP_PROJECT_ID: "my-project"
SECRET_NAMESPACE: "decisionbox"

extraEnvFrom:
- secretRef:
name: decisionbox-api-secrets

serviceAccountAnnotations:
iam.gke.io/gcp-service-account: "decisionbox-prod-api@my-project.iam.gserviceaccount.com"

resources:
requests:
cpu: "250m"
memory: "1Gi"
limits:
cpu: "2000m"
memory: "4Gi"

Deploy with:

helm upgrade --install decisionbox-api decisionbox/decisionbox-api \
-f values-prod.yaml -n decisionbox

Note: MONGODB_URI and SECRET_ENCRYPTION_KEY come from the decisionbox-api-secrets K8s Secret via extraEnvFrom — not from the values file.

Security

All pods run with hardened security contexts:

  • Non-root user (UID 1000)
  • Read-only root filesystem (/tmp mounted as emptyDir)
  • No Linux capabilities (drop: ALL)
  • Seccomp profile: RuntimeDefault
  • Pod anti-affinity (distributes replicas across nodes)

The API service account has scoped RBAC permissions to manage agent Jobs:

ResourceVerbs
batch/jobscreate, get, list, delete
core/podsget, list

These permissions are namespace-scoped (Role, not ClusterRole).

Health Checks

Both charts configure liveness and readiness probes:

ServiceLivenessReadiness
APIGET /health — 15s initial, 30s periodGET /health — 5s initial, 10s period
DashboardGET /health — 15s initial, 15s periodGET /health — 5s initial, 10s period

The API also exposes GET /health/ready which checks MongoDB connectivity. You can use this as the readiness probe path if you want readiness to depend on the database connection.

Run Helm tests to verify connectivity:

helm test decisionbox-api -n decisionbox
helm test decisionbox-dashboard -n decisionbox

Updating

helm upgrade decisionbox-api decisionbox/decisionbox-api \
-f values-prod.yaml -n decisionbox

helm upgrade decisionbox-dashboard decisionbox/decisionbox-dashboard \
-n decisionbox

The API re-creates MongoDB indexes on startup (idempotent). No database migrations needed.

Uninstalling

helm uninstall decisionbox-dashboard -n decisionbox
helm uninstall decisionbox-api -n decisionbox
kubectl delete namespace decisionbox

Next Steps