Skip to main content
The unified Runlayer operator reconciles two custom resources in one controller: 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.
Do not install the unified operator on a cluster that already runs runlayer-deploy-operator or anysource-chart. Both reconcile MCPServer CRs. Cut over one cluster at a time — see Migration.

End-to-end flow

  1. Provision cluster features and external per-tenant dependencies (Postgres, Redis, buckets, IAM / Workload Identity, streams).
  2. Install the runlayer-operator Helm chart once per cluster.
  3. Create (or let the operator create) the platform namespace and seed required Secrets.
  4. Apply a RunlayerInstance (raw YAML, optional runlayer-instance Helm chart, or Terraform on AWS).
  5. Wait for status.phase: Ready, point DNS at the load balancer, then create MCPServer CRs 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 uses streamBackend: pubsub, set one project for the instance:
Do not put 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 via spec.serviceAccount: AWS (IRSA):
GCP (Workload Identity):
Prefer WI/IRSA over ECS-style assume-role env vars (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:
OCI (Customer Distribution — production): versions declared production-ready via the runlayer-operator-promote-customer-distribution workflow.
OCI (staging / pre-promote): continuous push on main in account 412677576004. Override image.repository to the staging operator image when using those charts.
From the monorepo chart:
CRDs ship in the chart crds/ directory and are applied on first helm install only:
  • platform.runlayer.com/v1alpha1RunlayerInstance
  • deploy.runlayer.com/v1alpha1MCPServer

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:
If CRDs were never Helm-managed, plain kubectl apply -f ... is enough.
Always apply CRDs from the same chart version you are upgrading to. Skipping CRD apply can leave the controller unable to reconcile new fields.

4. Prerequisites: namespace and Secrets

Install order for Secrets

The operator never creates, reads, or syncs Secret data (no secrets 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 named runlayer-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

Because envFrom 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 its images.* 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.
For 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

Phases: PendingInstallingMigrating / UpgradingReady (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.
OCI example:
Values: 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:
When deploy.enabled: true, platform policies come from RunlayerInstance; MCP policies come from each MCPServer.spec.security.networkPolicy.

Workload scheduling

  • spec.workloadScheduling — default nodeSelector / affinity / tolerations for platform pods + migration Job
  • spec.components.<name>.scheduling — per-component override (e.g. ToolGuard → GPU pool)

Default container resources

When components.<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 when enabled: true and a CPU or memory target is set (backend, frontend, worker, sentryRelay, llmGateway)
  • llmGateway gets a default HPA (70% CPU, min 1, max 3) when enabled and autoscaling.llmGateway is unset — set autoscaling.llmGateway.enabled: false to disable
  • spec.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 dedicated emptyDir 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.
To revert to inline audit delivery, set 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

  1. Prefer a greenfield cluster or a maintenance window.
  2. Install runlayer-operator.
  3. Create Secrets + RunlayerInstance; verify Ready.
  4. Migrate MCP workloads to MCPServer CRs in the tenant MCP namespace.
  5. Switch DNS to the new Ingress / ALB / GLB.
  6. Uninstall legacy runlayer-deploy-operator and anysource-chart on that cluster only.
Detailed plan: gradual migration plan.

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
Design references (GitHub):