agent-worker, its turns run the real CLI inside the agent-harness image, which carries all three CLIs, a headless Chromium, and a developer toolchain. Harness agents run on the executor fleet: a shared pool of warm executor pods where an agent is a directory on a large XFS volume and every turn runs in its own throwaway sandbox. That fleet has cluster prerequisites and a footprint of its own; this page gives the numbers so you can size a Hybrid or Air-Gapped cluster for it.
The base platform footprint is on the edition pages (Hybrid, Air-Gapped). Everything here is on top of those numbers.
The fleet is always rendered by both charts (there is no enable flag) and starts warm with one executor (
fleet.replicas: 1 on Hybrid, global.fleet.replicas: 1 on Air-Gapped). The Agent Controller sends a harness turn to the fleet whenever a live executor is registered. Keep it warm: the first turn of any agent is then seconds away, and idle agents cost only their directory. Where a cluster cannot host the privileged executor, set replicas: 0; on a live cluster the chart also idles the executor by itself when its StorageClass is missing (see Idle guard).sb-* workspace pod (agent-sandbox image); its sizing is unchanged and lives in the Agent workspaces accordion on the Hybrid page. The older always-on ag-* harness pod, one per Claude Code / Codex agent with its own 40 GiB volume, is the legacy path being removed; do not size new clusters for it.
Every new agent is a harness agent. A new conversation agent, whether created in the app, over the API or the SDK, or built by Omni, runs on a harness CLI unless the request names another framework, so plan fleet capacity for your whole agent population, not only for the agents you explicitly put on a CLI. Existing agents keep the runtime they have until someone moves them, and that move is one-way; what carries over and what does not is on Apps and Agents.
What the Fleet Is
The fleet is afleet-executor StatefulSet. Each replica is one pod with two privileged containers over one XFS volume: the executor, which runs turns, and a mounter sidecar, which binds each agent’s directory into the executor for the duration of a turn and enforces the per-agent disk quota. An agent’s directory lives on exactly one executor’s volume, and its turns always run there.
Per Executor
Per Turn
Each turn runs in its own cgroup. These are the ceilings the executor applies; an agent can raise its own in its harness settings, clamped to the platform caps.The comment above the
fleet block in both charts’ values.yaml still quotes older per-turn defaults (“2Gi / 2 vCPU / 256 pids for a CLI turn”). The values in the table are what the current agent-harness image applies; the chart comment is being corrected.Concurrency Caps
Node Prerequisites
The installer (install.sh) and the Air-Gapped preflight hook check all of these and warn (non-blocking) when one is missing; the executor then fails to schedule and the fleet has no live executor. A cluster that fails any one of them cannot run harness agents until it is fixed, so treat the list as required.
Autoscaling
Scaling is driven by concurrent turns, never by the number of agents. Withfleet.keda.enabled: true the chart renders a KEDA ScaledObject on the StatefulSet:
Because there is exactly one executor per node,
maxReplicas can never usefully exceed the number of nodes that fit an executor. Grow the node group and raise maxReplicas together. Without KEDA, set fleet.replicas to the number of executors you want and scale it by hand; on a sealed network a fixed replica count sized for peak is the recommended posture.
Safe Rollout, Drain, and Upgrades
A roll never kills a running turn
A rolling update, a KEDA scale-in and a plain pod delete all end in the sameSIGTERM to the executor pod, and the pod treats it as a drain, not a kill:
- The executor stops claiming work and its heartbeat says
draining. The Agent Controller routes around it: nothing new lands on it, whatever was queued for it goes back to the shared queue, and it still counts as fleet presence, so new turns wait for the replacement instead of starting anywhere else. - The turns in flight keep running for up to
fleet.executor.drainSeconds(3600 s by default). A turn still running when that window ends is stopped the way a user stop is: the CLI is interrupted, its session is kept, and the task endsstoppedwith “The executor is restarting; send the message again to continue where it left off.” Sending the message again resumes the session on the new pod. No task is left hanging as executing. - The mounter sidecar waits on the executor’s health endpoint until it reports draining with no turn in flight, and only then unbinds the agent directories. The pod grace (
fleet.executor.terminationGraceSeconds, 7500 s: the CLI run budget plus five minutes) is a ceiling, never a wait; the pod leaves as soon as both containers exit.
- Raise
drainSecondstoward the run budget if your turns run long; it must stay at least 120 s under the pod grace (the Air-Gapped chart refuses to render otherwise). - Never shorten a termination with
--grace-period=0or--forceon a pod that still has turns; that is the one way to kill a turn. - Plan image rolls for quiet hours when you can; they are safe at any time, only slower under load.
- The drain needs an
agent-harnessimage that carries it; an older executor ignores the setting and exits as before.
Idle guard when the StorageClass is missing
The executor becomes Ready only when its node can run the privileged pod and its volume binds on an XFS +prjquota StorageClass. So that an install or upgrade never waits on an executor that cannot bind, both charts guard themselves on a live cluster:
helm template and dry-runs always render the configured replicas; the guard reads the live cluster only. Create the class (or point provisioner at your cluster’s CSI driver, or bring your own class) and the next upgrade brings the executor up. Nothing else about the release changes while the executor idles: harness turns queue until an executor registers.
--wait, --atomic and the installer
helm upgrade --wait (and --atomic, which implies it) waits for the executor StatefulSet like any other workload. Two consequences:
- A running turn holds the roll. With
--wait, an upgrade during a long turn can take up to the drain window plus pod start. Give--timeoutat least the drain window (3600 s by default) or upgrade in a quiet period. - An idle executor never stalls an upgrade. With the idle guard above, a cluster without the storage prerequisites renders the executor at zero replicas and the upgrade completes.
install.sh handles both for you: a first install runs without --wait or --atomic (the post-install smoke hook is the readiness gate) and its --fleet checks only warn; upgrades run helm upgrade --atomic with the installer’s timeout and then verify workloads, skipping zero-replica ones.
Capacity Planning
Use the cloud reference executor (12 vCPU / 100 GiB, 10 slots, one per 16 vCPU / 128 GiB node) as the unit. It keeps every turn’s 8 GiB ceiling fully backed, and the roughly 28 GiB of node headroom covers the kubelet, DaemonSets, the mounter, the 4 GiB/dev/shm and the 20 GiB package cache on node disk.
How to read the table:
- Slots, not agents. A fleet with one executor serves hundreds of idle agents; what it cannot do is run more than 10 (or
turnsMax) turns at once. Extra turns queue and start as slots free. - Agents per volume is a ceiling, not a target. Most agent directories stay far below the 5 GiB quota, so a 500 GiB volume holds many more than 90 agents in practice; it also carries the shared read-only CLI, skills and runtime-environment layers.
- Memory headroom. Keep the executor’s memory limit at or below about 80 % of the node so the node never evicts it, and keep
turnsMax x 8 GiBat or below that limit if you want every turn’s ceiling backed. - Smaller executors are fine. On the chart defaults (8 vCPU / 28 GiB limit, 12 slots) one executor fits on a 16 vCPU / 32 GiB node, at the price described in the warning above. Lower
turnsMaxto 3 or 4 there if your turns run heavy builds. - Workspace pods are separate. Agents that also use workspace tools still add their
sb-*pod while active (0.25 vCPU / 256 Mi request, up to 2 vCPU / 3 GiB, 5 GiB volume); size those from the Hybrid page’s workspace accordion.
Image and Registry
Both fleet containers use this one image. On a fresh node the first executor pays the full pull, so budget the ~1.5 GB registry transfer per executor node and, on Air-Gapped, make sure the mirror holds it at the pinned tag before the first executor starts.
Network Egress
Executors hold no LLM provider key. The only LLM path is the Agent Controller’s/llm-proxy, reached over the cluster network, and the controller is the only component that egresses to a model provider.
The executor also blackholes the instance metadata endpoint (
169.254.169.254) at boot and refuses to register if it cannot, so a turn can never read node credentials.
LLM Provider Requirements
Claude Code speaks to Anthropic; Codex speaks to OpenAI; OpenCode takes any<provider>/<model> slot the proxy routes. Credentials live only on the Agent Controller.
Anthropic direct
An Anthropic API key on the controller (the same LLM-key wiring as the rest of the platform). The controller egresses toapi.anthropic.com; override the upstream with LLM_PROXY_ANTHROPIC_BASE_URL for a gateway of your own.
Anthropic on Amazon Bedrock
Claude Code can run Anthropic models on Bedrock through the proxy’samazon_bedrock leg. The controller resolves AWS auth in this order, and no executor ever sees it:
On Air-Gapped there is no public Bedrock: point
LLM_PROXY_AMAZON_BEDROCK_BASE_URL at a VPC (PrivateLink) Bedrock runtime endpoint and set the matching region, or leave Bedrock off. The Air-Gapped chart exposes all of this under global.llm.bedrock: region, bearerTokenSecret (a Secret you create carrying a Bedrock API key), assumeRoleArn and assumeRoleExternalId (the STS path), and baseUrl (the VPC endpoint the amazon_bedrock leg dials on a sealed install). Leave the token and the role empty to use the pod’s own credential chain (EKS Pod Identity or IRSA). On Hybrid, set the same variables on the Agent Controller through the chart’s env values.
Hybrid has no Bedrock relay. xpander cloud relays credentials for the vendor providers only, never Bedrock, so on a Hybrid install new Claude Code agents run on the relayed Anthropic key. Claude Code on Bedrock there requires an organization-level Bedrock key added in Settings > LLM (bring your own key), on an account with the opt-in above.
The keyless (role / Pod Identity) methods above apply to the harness LLM proxy. The platform’s own agent-worker path has a separate constraint on keyless Bedrock; see Amazon Bedrock: key vs IAM on the Hybrid page.
Which CLI, provider and model a new agent gets
A create that names a framework, a provider, a model or harness settings is honored as written. A create that names none of them is defaulted, in the same order everywhere (cloud, Hybrid, Air-Gapped):- The organization’s default LLM (Settings > LLM). Anthropic maps to Claude Code on that model; Amazon Bedrock with an Anthropic model id maps to Claude Code on Bedrock; OpenAI maps to Codex; any other provider the LLM proxy routes maps to OpenCode with that
<provider>/<model>slot. A provider no CLI can run, or no org default at all, falls through. - The install’s default knobs (env on the
agentsandagent-controllerservices; on Air-Gapped the chart values below). - The baked defaults: Claude Code on Anthropic direct with Fable 5.1 (
claude-fable-5-1), Codex ongpt-6-astra, OpenCode onanthropic/claude-opus-5.
Air-Gapped vs Hybrid
Recommended for both editions: keep the fleet warm (at least one executor always up). The first turn of any agent is then seconds, and idle agents cost only their directory. A cluster that cannot meet the prerequisites should set
replicas: 0 (or let the idle guard do it) and fix the missing capability before opening harness agents to its users, since every new agent lands on the fleet.
Next Steps
Hybrid Edition
Base platform sizing, the workspace pod, the calculator, and the installer
Air-Gapped Edition
Sizing tiers, registry mirroring, and scaling with usage
EKS Cluster Setup
Node groups, the EBS CSI driver, and StorageClasses
Two-File Licensing
How seat and agent caps are enforced on a sealed install

