Kubernetes (Helm)

Deploy Gamut on your own Kubernetes cluster with the official Helm chart -- a long-lived host-app pod that orchestrates per-agent pods over a shared RWX volume, with built-in multi-user auth.

The Helm chart deploys Gamut as a single-tenant install on your own Kubernetes cluster. Unlike the Docker options, which run every agent as a sibling container on one host, the Kubernetes runtime schedules each agent as its own pod through the in-cluster Kubernetes API, so agents can spread across the nodes of a multi-node cluster.

The chart is published as a public repository: github.com/SkillfulAgents/k8s-runtime-helm.

When to choose this option

  • You already run workloads on Kubernetes and want Gamut to live alongside them.
  • You need agents to schedule across multiple nodes rather than one host.
  • You want a multi-user deployment with login, managed by your cluster's ingress, TLS, and RBAC.
  • You want garbage collection, network isolation, and resource limits handled by the platform.

For single-machine deployments, see Single-User Docker or Auth Mode.

Architecture

namespace
├── Deployment  host-app (1 replica, SQLite single-writer)
│     └── creates/deletes ──▶ Pod  agent-<id>  +  Service agent-<id>
├── Service + Ingress  →  host-app UI/API
├── PVC  (shared, RWX)  ←  host-app data dir + agent workspaces
├── ServiceAccount + Role/RoleBinding  (pods/services CRUD)
└── NetworkPolicy  (agent ingress confined to host-app)

The host-app and every agent pod share one PVC: the runtime mounts each agent's workspace subPath into the agent pod. The volume must support real POSIX semantics (random writes, fcntl locking) because agents run SQLite, git, and npm in the workspace -- plain object storage will not work.

On a multi-node cluster the PVC must be ReadWriteMany so host-app and agent pods can mount it from different nodes. A single-node cluster can use ReadWriteOnce.

Prerequisites

  1. SuperAgent v0.3.45 or later -- the first release that ships the Kubernetes runtime. The chart defaults to these images (0.3.45-auth host-app, 0.3.45 agent container); older versions do not have the runtime.
  2. A Kubernetes cluster (1.24+) with kubectl and helm configured against it.
  3. A ReadWriteMany StorageClass (NFS, Longhorn, CephFS, AWS EFS, Azure Files, GCP Filestore, …) for multi-node clusters. A single-node cluster can set storage.accessMode=ReadWriteOnce.
  4. A way to reach host-app. By default the chart only creates a ClusterIP Service. Public exposure (ingress, load balancer, DNS, TLS) is your cluster's job -- see Exposing host-app.
  5. An LLM provider API key. Self-host defaults to Anthropic. You can provide it at install time or add it later in the UI.

Quick start

Clone the chart and install it. The commands below use the local chart path ./k8s-runtime-helm; an OCI-published chart will come later.

git clone https://github.com/SkillfulAgents/k8s-runtime-helm.git
cd k8s-runtime-helm
 
helm install superagent ./k8s-runtime-helm \
  --namespace superagent --create-namespace \
  --set hostPublicUrl=https://superagent.example.com \
  --set storage.storageClassName=<your-rwx-class> \
  --set-string secrets.env.ANTHROPIC_API_KEY=sk-ant-...

Then port-forward to reach host-app during setup:

kubectl -n superagent port-forward svc/superagent 8080:80
# then open http://localhost:8080

The first user to sign up becomes the admin.

Providing secrets

Helm does not read a .env file. You can start without an LLM key, sign in as the first admin, and add the key in the UI before starting agents. To make the key available at first boot, use one of two approaches.

Option A — let the chart create the Secret. Pass keys via secrets.env; the chart renders a Kubernetes Secret and wires it into host-app with envFrom:

helm install superagent ./k8s-runtime-helm \
  --namespace superagent --create-namespace \
  --set hostPublicUrl=https://superagent.example.com \
  --set storage.storageClassName=<your-rwx-class> \
  --set-string secrets.env.ANTHROPIC_API_KEY=sk-ant-...

Avoid putting real keys in a values file you commit. Use --set-string on the CLI, or Option B.

Option B — reference a Secret you created (keeps keys out of Helm entirely):

kubectl create namespace superagent
kubectl -n superagent create secret generic superagent-secrets \
  --from-literal=ANTHROPIC_API_KEY=sk-ant-...
 
helm install superagent ./k8s-runtime-helm \
  -n superagent \
  --set hostPublicUrl=https://superagent.example.com \
  --set storage.storageClassName=<your-rwx-class> \
  --set secrets.existingSecret=superagent-secrets

The Secret's keys become env vars on host-app verbatim, so name them exactly as the provider expects (e.g. ANTHROPIC_API_KEY, OPENROUTER_API_KEY, AWS_BEARER_TOKEN_BEDROCK).

Exposing host-app

By default the chart creates a ClusterIP Service, so host-app stays reachable inside the cluster without being exposed publicly. When you want public access, pick whatever fits your cluster:

  • Point your existing ingress controller at the superagent Service, or
  • Set service.type=LoadBalancer to get a cloud load balancer, or
  • Enable the example Ingress and fill in settings for your controller:
ingress:
  enabled: true
  className: alb            # or nginx / traefik / … — no default
  host: superagent.example.com
  annotations:              # provider-specific; e.g. for AWS ALB:
    alb.ingress.kubernetes.io/scheme: internet-facing
    alb.ingress.kubernetes.io/target-type: ip
  tls:
    enabled: true
    secretName: superagent-tls

Authentication (multi-user login)

auth.mode defaults to true -- host-app runs as a multi-user deployment with login. No OIDC setup is required: it uses email/password out of the box, and the first user to sign up becomes the admin. By default further signups are invitation-only and need admin approval (tunable in host-app's auth settings).

To add OIDC/social login, set AUTH_PROVIDERS_JSON. It carries a clientSecret, so put it in secrets.env or your existingSecret, never in a committed values file. See Auth Mode for the full provider schema -- the same configuration applies here.

To run a single-user instance with no login, set auth.mode=false.

Enabling auth requires a clean data directory on first boot. Enabling it on a PVC that already holds non-auth agents is rejected at startup.

Key values

KeyDefaultNotes
hostPublicUrl""Required. External URL of the host-app.
auth.modetrueMulti-user login (email/password by default). false = single-user.
storage.existingClaim""Bring-your-own PVC; chart skips creating one.
storage.subPath""Host-app data subPath when sharing one backing filesystem.
storage.storageClassName""RWX-capable class; "" = cluster default.
storage.accessModeReadWriteManyReadWriteOnce only on single-node.
secrets.env{}Provider keys injected as env (e.g. ANTHROPIC_API_KEY).
secrets.existingSecret""Reference a pre-created Secret instead.
ingress.enabledfalseExample Ingress; off by default. Expose host-app yourself.
ingress.className""Your ingress controller class (alb / nginx / …). No default.
agent.resourcescpu: 1, memory: 2GiPer-agent pod resources.
networkPolicy.enabledtrueConfine agent ingress to host-app.

See values.yaml in the chart repo for the full list.

Notes

  • hostApp.replicas is pinned to 1 -- host-app is a single-writer over the shared PVC.
  • settings.json is seeded only on first boot; later changes to hostApp.settings won't re-apply.
  • Agent pods are garbage-collected via an ownerReference to the host-app pod: if host-app dies, the cluster cleans up orphaned agent pods.

Next steps