Self-Hosting

Kubernetes (Helm)

Deploy Gamut on your own Kubernetes cluster with the Helm chart. A single host-app pod schedules each agent as its own pod over a shared RWX volume.

Unlike the Docker options, which run every agent as a sibling container on one host, the Kubernetes runtime creates and deletes pods through the in-cluster API, so agents can spread across the nodes of a multi-node cluster. The runtime is selected automatically when Gamut runs inside a cluster. The chart is maintained in a separate public repository: github.com/SkillfulAgents/k8s-runtime-helm.

When to choose this option

  • Gamut lives alongside existing Kubernetes workloads.
  • Agents need to schedule across multiple nodes rather than one host.
  • A multi-user deployment with login, managed by the cluster's ingress, TLS, and RBAC.
  • 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. A recent Gamut release: the Kubernetes runtime ships in recent releases, and the chart's default image tags pin a version that includes it.
  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, and others) for multi-node clusters. A single-node cluster can set storage.accessMode=ReadWriteOnce.
  4. A way to reach host-app: the chart only creates a ClusterIP Service by default. See Exposing host-app.
  5. An LLM provider API key: self-hosting defaults to Anthropic. Provide it at install time or add it later in the UI.

Quick start

Clone the chart and install from the local path (an OCI-published chart is planned):

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. Starting without an LLM key works: 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. For public access, pick whatever fits the cluster:

  • Point an 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: email/password works out of the box. 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