Use this path for one or many isolated tenants on a shared Kubernetes cluster (EKS or GKE). For legacy single-tenant Helm installs, see Helm + Kubernetes (
anysource-chart) until you cut over.
End-to-end flow
- Provision cluster features and external per-tenant dependencies (Postgres, Redis, buckets, IAM / Workload Identity, streams).
- Install the
runlayer-operatorHelm chart once per cluster. - Create (or let the operator create) the platform namespace and seed required Secrets.
- Apply a
RunlayerInstance(raw YAML, optionalrunlayer-instanceHelm chart, or Terraform on AWS). - Wait for
status.phase: Ready, point DNS at the load balancer, then createMCPServerCRs if using Runlayer Deploy.
1. External dependencies
The operator does not provision cloud infrastructure. Each tenant needs the following out-of-band (Terraform, console, or your platform).Required for every tenant
Network: cluster nodes (or pods via CNI) must reach Postgres and Redis on private connectivity (VPC, Private Service Connect, peering). Tighten egress with
spec.security.networkPolicy.additionalEgressRules once CIDRs are known.
Optional (feature-dependent)
AWS helper modules (optional):
GCP: use equivalent Cloud SQL / Memorystore / IAM / GKE Ingress resources; wire the same Secret names and CR fields. There is no first-party GCP Terraform module yet — follow the CR examples below.
Shared GCP project
When any component usesstreamBackend: pubsub, set one project for the instance:
projectId on per-component pubsub blocks (subscription / DLQ only).
Identity model (IRSA / Workload Identity)
The operator creates dedicated ServiceAccounts per workload and never embeds long-lived cloud keys. Annotate viaspec.serviceAccount:
AWS (IRSA):
AUDIT_LOG_SIEM_EXPORT_ASSUME_ROLE_ARN is not the K8s path).
2. Cluster requirements
Kubernetes version
Shared features (EKS and GKE)
EKS-specific
See also EKS + Terraform.
GKE-specific
Namespace layout
Override with
spec.deploy.platformNamespace / spec.deploy.mcpNamespace.
3. Install and upgrade the operator
Install (once per cluster)
Copy and customize values:runlayer-operator-promote-customer-distribution workflow.
main in account 412677576004.
Override image.repository to the staging operator image when using those charts.
crds/ directory and are applied on first helm install only:
platform.runlayer.com/v1alpha1—RunlayerInstancedeploy.runlayer.com/v1alpha1—MCPServer
Common operator values
Full reference: chart README and CHART-DOCS.md.
Upgrading the operator (manual CRD apply)
Helm does not upgrade CRDs on subsequent releases. When a release notes CRD schema changes, apply CRDs before upgrading the chart:kubectl apply -f ... is enough.
4. Prerequisites: namespace and Secrets
Install order for Secrets
The operator never creates, reads, or syncs Secret data (nosecrets write RBAC). You must create Secrets in the platform namespace.
Two workable sequences:
AWS Terraform path (
terraform-runlayer-instance) uses A (TF owns the namespace + Secret sync from Secrets Manager).
Required Secrets (platform namespace)
Example (sequence A):
Optional component Secrets
App secret
Platform Secret namedrunlayer-app by convention (spec.appSecretRef.name). Mounted with envFrom on backend and worker (and related backend-image jobs). Every key in the Secret becomes an environment variable.
Required keys
Common optional keys
AUTH_API_KEY may also appear here; it is also injected from spec.auth.apiKeyFrom (duplicate is harmless).
Common non-secret configuration via the same Secret
BecauseenvFrom loads all keys, teams often put non-sensitive settings in runlayer-app as well (or use a separate ConfigMap pattern outside the operator). Typical production keys:
Any backend
Settings field (see application config) can be supplied this way. Prefer CR fields for connectivity that the operator already knows how to wire (database, redis, auth, component toggles).
On AWS, invoking Anthropic models on Bedrock needs a one-time form submission in the account those calls are made from, even when BEDROCK_ENABLED is false:
One-time AWS step. Anthropic requires a use case details form before its models can be invoked in an AWS account, and Runlayer Assistant always routes through Bedrock — so this applies even when
BEDROCK_ENABLED is false.Submit it in the account the Bedrock calls come from (your deployment’s account, or the account behind a Bedrock AI provider role you configure): Bedrock console → Model catalog → any Anthropic model → Submit use case details. AWS accepts it once per account, or once at your AWS Organization’s management account — member accounts inherit that.Deploying outside the US? Also set RUNLAYER_ASSISTANT_BEDROCK_MODEL to your geography’s inference profile, for example au.anthropic.claude-opus-4-6-v1 in ap-southeast-2.If skipped, this leaves Bedrock-backed agents failing with Model use case details have not been submitted for this account — see troubleshooting for the CLI checks and Region caveats.5. Deploy a RunlayerInstance
Required spec fields
Optional highlights:
database.port (5432), database.name (runlayer), redis.port / tls / credentials, components, deploy, serviceAccount, workloadScheduling, security, trustedCa, autoscaling, podDisruptionBudget, ingress, otelCollector, auditSpool, deploymentTelemetry.
Authoritative field-level reference: RunlayerInstance CRD design.
Minimal example (EKS)
Minimal example (GKE)
Optional components (snippet)
When enabling an optional component, set itsimages.* field (and any required Secret refs). The operator rejects the CR if those are missing. status.phase: Ready / Available wait for every enabled optional Deployment (not only backend/frontend/worker).
Pull credentials for platform components: the operator sets no imagePullSecrets on platform component pods (spec.deploy.defaultImagePullSecrets applies to MCPServer pods only — the Helm chart’s toolguard.image.pullSecrets knob has no operator equivalent). The Customer Distribution ECR references above therefore only work when the cluster’s node role can pull cross-account (EKS in an AWS account belonging to the Runlayer org). Clusters outside the org — including all GKE — must point images.* at a registry they can reach: the GAR mirror or a mirror in their own registry.
streamBackend: kinesis on auditConsumer / siemExport / sessionMaterializer, replicas must be ≤ 1 (CEL). Session materializer is always singleton (CEL) for both backends — compaction safety. It also injects SESSION_* / HOOK_EVENTS_* into backend/worker (payload read + hook publish). For Pub/Sub, provision the hook-events subscription with message ordering enabled; publishers use ordering_key=session_id. Publish RPC timeout defaults to 5s (HOOK_EVENTS_GCP_PUBSUB_PUBLISH_TIMEOUT_SECONDS); override via that env or hookEvents.publishTimeoutSeconds on the CR. Infra (streams, checkpoints, buckets, ordered subscription) is out-of-band; see the CRD design — Session materializer. spec.agents env (RUNLAYER_AGENT_SANDBOX*) is injected onto backend and worker (and onto agentEventTrigger when that component is enabled) — see the CRD design — Agents.
K8s Agent Sandbox (sandboxMode: k8s)
In-cluster runtime using kubernetes-sigs/agent-sandbox (SandboxClaim + SandboxWarmPool). The operator only wires env; it does not install the controller, Template, WarmPool, or isolation runtime.
1. Install the controller
2. Isolation on the nodes (gVisor or Kata — pick one)
Sandbox pods must not use the host
runc kernel. Nodes that schedule them need a RuntimeClass (kubectl get runtimeclass):
GKE add-on admission also requires
automountServiceAccountToken: false, runAsNonRoot, drop ALL capabilities, CPU+memory limits, and nodeSelector/toleration sandbox.gke.io/runtime=gvisor.
3. Template + WarmPool + RBAC, then set spec.agents.sandboxMode: k8s + spec.agents.k8s.warmPool. Spike manifests and both RuntimeClass snippets: docs-internal/agent-sandbox-k8s/ (README).
Deploy (spec.deploy)
When enabled, backend/worker get
RUNLAYER_DEPLOY=K8S and related env; frontend gets PUBLIC_RUNLAYER_DEPLOY=K8S.
Wait for Ready
Pending → Installing → Migrating / Upgrading → Ready (or Degraded / Failed).
Available=True only when every enabled core and optional component Deployment is Ready (for example ToolguardReady, TopicCpuReady, TopicCpuFastReady, LLMGatewayReady). Disabled optionals are ignored. Ingress is separate: when Ingress is enabled, also watch IngressReady for traffic (on GCE, that includes the llm-gateway GLB backend when the Ingress routes to it).
Then create MCP env Secrets in the MCP namespace and apply MCPServer CRs (see MCPServer design).
6. Optional: runlayer-instance Helm chart
The runlayer-instance chart renders one RunlayerInstance CR per Helm release. Prefer it for GitOps when you do not use Terraform.
v1 owns only the CR — Secrets and (when not using
spec.ingress) Ingress stay outside Helm.
name, namespace, labels, annotations, and spec (full RunlayerInstanceSpec passthrough). Do not put tenant CRs in the operator chart — operator upgrades must not re-render tenants.
7. Ingress, NetworkPolicy, scheduling, resources
Ingress
Opt-in (spec.ingress.enabled, default false). When enabled, the operator reconciles path routing:
On
gce / gce-internal, the operator creates BackendConfigs (backend, frontend, and llm-gateway when enabled) before annotating Services / applying Ingress so GKE GLBC does not stick on missing BackendConfig. Set controllerNamespace so NetworkPolicies allow the ingress controller → backend (AWS LBC, nginx). GKE also uses GCP health-check CIDR defaults.
Do not enable certManager and gcp.managedCertificate on the same instance.
NetworkPolicies
Defaults isolate tenants and allow required DNS / platform / MCP paths. Extensions are append-only:deploy.enabled: true, platform policies come from RunlayerInstance; MCP policies come from each MCPServer.spec.security.networkPolicy.
Workload scheduling
spec.workloadScheduling— defaultnodeSelector/affinity/tolerationsfor platform pods + migration Jobspec.components.<name>.scheduling— per-component override (e.g. ToolGuard → GPU pool)
Default container resources
Whencomponents.<name>.resources is omitted, operator defaults match anysource-chart:
Override per component for smaller clusters. Topic CPU also enforces ECS-parity memory floors vs
gunicornWorkers / fast.gunicornWorkers. If you set deploy.resourceQuota.hard, ensure requests fit the quota.
Autoscaling and PDBs
spec.autoscaling.<component>— HPA whenenabled: trueand a CPU or memory target is set (backend,frontend,worker,sentryRelay,llmGateway)llmGatewaygets a default HPA (70% CPU, min 1, max 3) when enabled andautoscaling.llmGatewayis unset — setautoscaling.llmGateway.enabled: falseto disablespec.podDisruptionBudget— optional PDBs for core Deployments
Trusted CA
spec.trustedCa mounts a customer CA ConfigMap and sets SSL_CERT_FILE / REQUESTS_CA_BUNDLE / NODE_EXTRA_CA_CERTS for enterprise TLS inspection.
OTEL collector sidecar
spec.otelCollector.enabled injects a native sidecar on the backend Deployment (Kubernetes ≥ 1.29). Requires reachable OTLP endpoints; hostname endpoints may need extra NetworkPolicy CIDRs. See the CRD design — OTEL.
Audit log spool
The backend’s local audit-log spool is enabled by default: audit records are appended to a spool on the pod’s local disk (a dedicatedemptyDir at /var/audit-spool) and delivered asynchronously by an in-process tailer, so MCP proxy requests do not wait on audit delivery. Records are typically delivered within seconds, and delivery is at-least-once with deduplication. If the spool cannot keep up or its disk budget fills, individual records fall back to inline delivery on the request path, so nothing is lost to backpressure.
- Node-disk encryption is required. The spool briefly holds pre-redaction audit payloads at rest on the node’s disk. On GKE, node disks are encrypted by default with Google-managed keys (CMEK optional). On EKS, encrypted EBS node volumes must be explicitly enabled. The operator cannot verify either.
- The backend pod’s shutdown drain window for the spool is 110 seconds (grace period 140s minus 30s headroom); records not delivered before the window expires are lost with the pod.
- After an outage of a delivery target (database or Kinesis), expect elevated write load while each backend pod drains its accumulated backlog. On small database instances,
AUDIT_LOG_SPOOL_DELIVERY_CONCURRENCY(default 8, per pod) can be lowered via the app secret to throttle the catch-up. - Each backend pod can hold up to ~512MB of spool data on node disk (
AUDIT_LOG_SPOOL_MAX_BYTES; dead-letter bytes count against the same budget). Budget node ephemeral storage accordingly when running many instances per cluster.
spec.auditSpool.enabled: false. New pods write audit logs inline again, and each old pod drains its residual spool within the shutdown window as it stops. No data migration is needed in either direction.
Disabling requires the operator 0.2.22 CRD: helm upgrade does not apply crds/, and against an older CRD the API server silently prunes spec.auditSpool, leaving the spool on. Apply the updated CRD first — see Upgrading the operator.
Migration from legacy charts
- Prefer a greenfield cluster or a maintenance window.
- Install
runlayer-operator. - Create Secrets +
RunlayerInstance; verifyReady. - Migrate MCP workloads to
MCPServerCRs in the tenant MCP namespace. - Switch DNS to the new Ingress / ALB / GLB.
- Uninstall legacy
runlayer-deploy-operatorandanysource-charton that cluster only.
Related docs
Legacy Helm (single tenant)
anysource-chart for traditional single-tenant installs
EKS + Terraform
Provision EKS, RDS, and Redis before the operator
Deployment overview
Account strategy and isolation models
Network firewall
Client egress allowlists for the tenant hostname