Architecture
The AWS network boundary, PrivateLink, authentication and runtime flows, and the Hybrid → Cloud sync surface (cluster identity, unpublished heartbeat schema, agent catalog, skill schema) are on Hybrid Security Architecture. This section is the operational view: what the chart deploys, how clients connect, and the residency table for an install review. Hybrid follows the control plane / data plane separation pattern, the same model used by AWS EKS, Datadog, and other infrastructure platforms:- Control plane (xpander Cloud): manages environment metadata, agent registration, skill definitions, and event logging. Admins and team members use Xpander Chat to assemble agents from approved skills and validate tasks. The control plane never touches runtime data.
- Data plane (your VPC): runs all task execution, agent memory, skill credentials, model API keys, and user-facing services. The user’s browser connects directly to the data plane. Chat threads, activity views, and task results are served from the cluster: they do not pass through the cloud. Agent definitions and the skills catalog are authored in Cloud and pulled by the Agent Controller.
What Gets Deployed
Resource names on this page assume the release namexpander, as in every command here. With another release name, they start with <release>-xpander- instead, or with <release>- when the release name already contains xpander.
How It Works
Clients (Slack, the web UI, MCP clients, the SDK and API) connect directly to the cluster in your VPC. When usingchat.xpander.ai, the cloud UI handles authentication and tells the client which self-hosted cluster to connect to; chat, tasks, and results then flow client-to-cluster. Inside the cluster, the Agent Controller schedules tasks and the execution tier (the agent-worker service) executes them: calling LLMs, persisting messages in PostgreSQL, and reaching the external systems behind skills through the Agent Controller’s skill relay.
The only connection to xpander Cloud is the deployment manager sync: the Agent Controller authenticates with organization_id + environment_id + api_key, reports heartbeats, and pulls agent and skill (including MCP) configuration. The heartbeat JSON schema is not published. See Data that leaves the on-prem installation.
What Talks to What
The egress surface: the answers a security review needs. Inbound from xpander cloud: none; connections are outbound-only.Data Residency
Requirements
Sizing lives on one page: the minimum footprint, the sizing model and calculator, per-service requirements, the executor fleet and the data services are all on Sizing and requirements. The installer checks the same minimum numbers, published at charts.xpander.ai/requirements.json. The release namespace must admit PodSecuritybaseline (unlabelled is fine; restricted is refused), and the nodes that run the executor fleet need Landlock or fleet.landlock.enabled: false. See Node prerequisites.
Need a cluster? Follow the EKS Cluster Setup guide.
Bedrock needs your own credentials. Agents on a Hybrid install never use xpander’s keys (LLM Gateway). To run Claude Code on Bedrock here, add a Bedrock key or role under Platform > AI governance > Models, or give the cluster an AWS identity for Bedrock; see AI vendors.
Install
1. Get your one-liner from Xpander Chat
- In Xpander Chat, open Platform > Agents > Runtime and choose the Locations tab (Connected runtimes and regions)
- Click “Add Location”
- Choose the Hybrid edition. The dialog shows the minimum footprint for the deployment you picked.
- Optionally set a URL (your server’s FQDN, e.g.
xpander.my-company.com) and the hostname style (dash-based subdomains if your DNS can’t do wildcards) - Copy the generated one-liner
--org, --env, and --dm-api-key (plus --domain when you set a URL). That is how the cluster authenticates to the xpander control plane:
2. Run the Installer
Run it from a terminal withkubectl access to the target cluster. The command downloads the installer from charts.xpander.ai and runs an interactive wizard: preflight checks, configuration, Helm install, then the connection back to xpander. Run bare (curl -fsSL https://charts.xpander.ai/install.sh | bash), it is the zero-knowledge entry point: the first question is which edition you are installing.
The wizard walks through, in order:
Edition & requirements
--edition wasn’t given, it asks. It then prints the minimum footprint for that edition (the same numbers as charts.xpander.ai/requirements.json) and asks you to confirm your cluster meets it, stopping cleanly if not.Identity
Ingress domain (optional)
agent-controller.xpander.<DOMAIN>, needs wildcard DNS) or dash style (agent-controller-xpander.<DOMAIN>, individual A records).Control plane reachability
https://deployment-manager.xpander.ai), AWS PrivateLink (https://deployment-manager-privatelink.xpander.ai), or Staging. The installer auto-detects staging/local environments (a .stg. or local domain, or a kind/k3d/minikube/docker-desktop kube context) and leads with that option; any of --staging, --privatelink, or --dm-url overrides detection.LLM providers (all optional)
AWS_BEARER_TOKEN_BEDROCK), or keyless IAM role / EKS pod identity. See Amazon Bedrock: keyless IAM or API key.Namespace, context confirmation, summary
xpander, created if missing), an explicit confirmation of the kubectl context it is about to install into, and a masked configuration summary before anything changes.Install Phases
The Hybrid install runs six phases, each announced as a stable[N/6] name: ... progress line (the same shape scripts can grep, see Automation & CI):
--reset-then-reuse-values) needs Helm 3.14 or later.
Every failure prints a FIX: line naming the action that repairs it and a RERUN: line with your personalized non-interactive one-liner (secrets typed at prompts are placeholdered, never echoed). Fix the condition and paste the rerun line: the installer is idempotent.
Amazon Bedrock: keyless IAM or API key
--bedrock-auth iam is keyless: the pods sign Bedrock calls with their own AWS identity (EKS Pod Identity or IRSA) for chat, agent task executions and the summaries around them, supported since xpander-sdk 2.0.503 (images built after 27 August 2026). The role bound to the pods needs the Bedrock permissions itself (bedrock:InvokeModel, bedrock:InvokeModelWithResponseStream, bedrock:Converse, bedrock:ConverseStream on *) and must be bound to both ServiceAccounts the LLM services run as: default and agent-controller. The chat and task paths do not assume a second role. --bedrock-auth key (default when --bedrock-key is given) stores a bearer token instead. Either way the account must be opted in to Bedrock’s data retention mode for the models you use. The “IAM role” method on a provider under Platform > AI governance > Models is not the path for chat and tasks; it applies to the Claude Code leg described next. If chat fails with a Bedrock entry configured that way, remove the entry and restart the agent-controller, agent-worker, agents, actions and functions deployments.
Claude Code on Bedrock takes a different path. The CLI’s model calls go through the Agent Controller to the in-cluster LLM Gateway, and the controller resolves AWS auth itself, in this order: AWS_BEARER_TOKEN_BEDROCK, then BEDROCK_ASSUME_ROLE_ARN (STS, self-refreshing), then static keys, then the pod’s default credential chain (Pod Identity / IRSA). Set whichever you use as controller environment through the chart’s env values, with BEDROCK_REGION or AWS_REGION next to it. An organization that wants Claude Code on Bedrock adds its own Bedrock key under Platform > AI governance > Models; that key is preferred over the controller’s credential.
3. Verify the Connection
When the install completes, a summary box prints the release, control-plane URL, ingress state, and configured providers. Your cluster registers with the control plane and the xpander console flips to “Connection established” in a minute or two.Running, 1/1-ready pod per application service: agent-controller, agent-worker, ai-gateway, api, aws-operator, chat, code-runner, and mcp. ai-gateway is a registration-only compatibility component; skill calls, including AWS ones, are served by agent-controller since chart 0.16.1. The LLM Gateway runs as two llm-gw pods (one on a single-node cluster installed with install.sh). You should also see the postgres and redis StatefulSets, and, at defaults, docker-registry and metrics-server.
Test locally without ingress:
PrivateLink
Your cluster must reach the xpander deployment manager. Two options:--privatelink (or pick AWS PrivateLink at the wizard’s reachability question). --privatelink, --staging, and --dm-url are mutually exclusive: pass exactly one, or none for the public endpoint.
How PrivateLink Works
AWS PrivateLink is a two-sided connection. xpander exposes the deployment manager as a VPC Endpoint Service (provider). You create a VPC Endpoint (consumer) in your VPC, which provisions a private ENI with a10.x.x.x IP address. Your pods connect to this private IP: traffic goes over the AWS backbone, never the internet.
Set It Up
Create the VPC endpoint
VpcEndpointId, Groups[0].GroupId (the security group), DnsEntries[0].DnsName, and DnsEntries[0].HostedZoneId.Allow HTTPS on the endpoint security group
Wait for the endpoint to become available
pending to available in 1-3 minutes (auto-accepted):Create private DNS
deployment-manager-privatelink.xpander.ai resolves to the endpoint ENI inside your VPC:HostedZone.Id from the output, then create the alias record:Verify connectivity
000? See Troubleshooting below.External Access & DNS
The xpander services communicate with each other internally via Kubernetes Services: no ingress is needed for that. You only need external access so that users can reach the chat UI, API, and other endpoints from their browser or client.--domain (or set ingress.enabled=true in Helm values) and skip to DNS records.Option A: VPN / Corporate Network Access
If your organization has a VPN that routes into the VPC, users can access xpander services directly: no ingress controller, NLB, ACM certificate, or public DNS needed. Point internal DNS records to the Kubernetes services:Option B: Public Access via Ingress + NLB
For public or internet-facing access, set up an ACM certificate, an nginx ingress controller, and an NLB, using the three steps below: SSL Certificate, Ingress Controller, and DNS Records.SSL Certificate
Request an ACM certificate for your domain.<DOMAIN> / *.<DOMAIN> (shared) and one for *.chat.<DOMAIN>:
Ingress Controller
Install the nginx ingress controller with AWS NLB and ACM SSL termination:DNS Records
Create wildcard CNAME records pointing*.<DOMAIN> and *.chat.<DOMAIN> to the NLB hostname. Both are required: the chat UI generates per-thread subdomains under chat.<DOMAIN>.
LLM Gateway
The chart runs the xpander LLM Gateway (llm-gw) in your cluster, and it is always on. Agents send every model call through it. It applies your organization’s allowed models, budgets and usage reporting. Agents reach it in-cluster at http://xpander-llm-gw:4000, the Base URL that Platform > AI governance > Models shows while the host is not published. Configuration, publishing and the Hybrid specifics are on LLM Gateway on a self-hosted install.
secrets.static), and a call to a provider with neither is refused. The refusal names the missing key; set install keys as shown below. Custom OpenAI-compatible endpoints keep working as configured.
Health. New agent runs wait for a healthy gateway. If its pods cannot start (for example, the image cannot be pulled), new runs are refused with “The xpander AI gateway is unavailable right now” until the pods are ready:
llm-gw pods (one on a single-node cluster installed with install.sh), each 1/1 and Running.
Network. With networkPolicy.enabled, the gateway gets its own policy: ingress on port 4000 only, egress to DNS, agent-controller, Redis and public model providers over HTTPS, never a private or link-local address. Add what else it must reach:
llm-gw.<domain>. Agents never need it. Only to serve coding clients on laptops and SDKs inside your network, set llm-gw.ingress.enabled: true (it renders only with the chart-wide ingress.enabled). You then need a DNS record for the host and a certificate that covers it (the chart reuses ingress.tls.secretName). Any load balancer in front needs an idle timeout of at least 1500 seconds (ALB: idle_timeout.timeout_seconds=1500). llm-gw.ingress.internal: true serves the host on a private load balancer (internalClassName, internalAnnotations). While no gateway pod is ready, a small xpander-llm-gw-unavailable pod answers the published host with a JSON 503 that coding clients and SDKs retry. Set llm-gw.unavailable.replicas: 2 to keep it through a node loss.
It cannot be turned off. From chart 0.22.5 the chart ignores llm-gw.enabled; charts 0.22.0 to 0.22.4 still read it. You can leave the key in your values or remove it. Every helm upgrade, including --reuse-values, starts the gateway.
Managing LLM API Keys
Usehelm upgrade with --reset-then-reuse-values (Helm 3.14 or later) to add or change API keys: it keeps the values you set and applies the chart’s defaults for everything else:
kubectl get pods -n xpander.
Your own keys and credits
Keys can also live in the product rather than the chart: an organization-level key per provider under Platform > AI governance > Models, or an agent-level credential on one agent. Whenever a model call runs on a key you brought (agent-level first, then the org-level key), it is never charged credits; xpander still measures the usage, so budgets, usage reports and the task’s token panel stay accurate. Which key a call needs is under LLM Gateway. Agents on Claude Code, Codex or OpenCode resolve keys the same way through the Agent Controller and the LLM Gateway, so the same rule applies to their turns.Secret not updating after helm upgrade?
Secret not updating after helm upgrade?
xpander-static secret has a Helm resource keep policy, so helm upgrade may not update it on subsequent installs. If your API key isn’t being picked up after a helm upgrade, set it directly in the secret:Key names read from an environment variable
An agent, a custom model provider or the organization’s default model can name its API key by an environment variable on agent-controller (agent-controller.env or global.env, for example MY_VLLM_TOKEN). Such a name never reads one of the install’s own credentials. These names read as no key:
- names starting with
AWS_(exceptAWS_BEARER_TOKEN_BEDROCK),XPANDER_,FLEET_,LLM_GW,LLM_GATEWAY,LITELLM_,GITHUB_APP,SUPABASE,REDIS,OAUTH,JWT,LICENSE,MONGO,POSTGRES,PG_,ON_PREM_PG,SMTP,REGISTRY,SANDBOX_,HARNESS_orKUBE - names ending with
_SERVICE_API_KEY,_PASSWORD,_PRIVATE_KEY,_SECRET,_SECRET_KEY,_SESSION_TOKENor_TOKEN_FILE - the platform’s own service keys (
DEPLOYMENT_MANAGER_API_KEY,AGENT_CONTROLLER_API_KEYand the like)
OPENAI_API_KEY, ANTHROPIC_API_KEY, AWS_BEARER_TOKEN_BEDROCK, GOOGLE_API_KEY, AZURE_OPENAI_API_KEY, OPENROUTER_API_KEY and the other provider keys) keep working, and so does any other name, such as VLLM_API_KEY. Rename a name that matches the list (for example VLLM_SECRET to VLLM_API_KEY) in both places: the agent-controller env that carries the value and the setting that names it.
Model endpoints on metadata addresses are refused. A model endpoint set on an agent, a custom provider, the organization’s default model or an Azure key is refused when it resolves to a metadata or link-local address (169.254.0.0/16, fe80::/10, the cloud metadata hosts). The check runs when the endpoint is saved and again on every call, against the address actually dialled. Private addresses (a vLLM or model gateway on your own network) keep working.
Using Kubernetes Secrets (Recommended for Production)
Using the SDK with Self-Hosted
Set thebase_url field on the xpander SDK’s Configuration object to point at your Agent Controller endpoint, as shown below.
Using the SDK against your install
base_url points to the Agent Controller endpoint (e.g., https://agent-controller.{your-domain}), not the root domain.For more SDK examples, see the Self-Hosted SDK Configuration.Upgrading
Upgrades work like the Datadog agent flow: watch the xpander releases feed (Releases page / Atom feed), then run theupgrade subcommand:
install.sh upgrade:
- finds the release (default
xpanderin namespacexpander; override with--release/--namespace), - infers its edition from the installed chart: you never pass
--editionto an upgrade, - reuses the values the release was installed with (
helm get values), plus anything extra you pass (--set,--values), - shows current → target version with a link to the release notes, and asks before changing anything,
- always runs with
--atomic: a failed upgrade (including a failed hook) rolls back automatically and the previous revision keeps serving.
--reset-then-reuse-values keeps the values you set and applies the new chart’s defaults for everything else. Plain --reuse-values replays the previous release’s values, including the old chart’s defaults, so it misses defaults that changed (see below).
Upgrading to the current chart
The current Hybrid chart isxpander 0.22.5 (release v2026.10.22). Database migrations run automatically. Upgrade with Helm 3.14 or later:
- In-cluster Postgres moves to the pgvector build (0.22.1). The
postgresStatefulSet runspgvector/pgvector:0.8.7-pg15-trixieinstead ofpostgres:15: PostgreSQL 15.19, the same major version, on the same volume. If you useglobal.imageRegistry, mirrordocker.io/pgvector/pgvector:0.8.7-pg15-trixiefirst. Take a volume snapshot ofpostgres-data-xpander-postgres-0, or setpostgres.upgradeGuard.backup.pvcNameto an existing PVC with room for a full dump. Postgres restarts once. If you already run 0.22.1 or later, Postgres is on pgvector and this step is done. - A pinned Postgres image stays on pgvector (0.22.1). If your values set
images.postgres, keep it on a pgvector build of PostgreSQL 15. A pre-upgrade check refuses the upgrade when the major version would change. - A new key in the generated Secret (0.22.3).
BRAIN_INGEST_API_KEYis added automatically and kept from then on. No action needed. - Environment from your own Secrets and ConfigMaps renders (0.22.4).
global.envFromSecret,global.envFromConfigMapand the per-componentenvFromSecretandenvFromConfigMaplists now render as the container’senvFrom. Earlier charts failed the render when they were set. Installs that never set them render the same as before. - The LLM Gateway is always on (0.22.5). The chart ignores
llm-gw.enabled. If your install hadllm-gw.enabled: false, mirror thellm-gwimage if you mirror images, make sure every model provider your agents use has a key, and open the gateway’s network paths (LLM Gateway). The upgrade starts the gateway pods and restarts agent-controller and api once. Then checkkubectl -n <namespace> get pods -l app=llm-gw. If the gateway was already on, nothing changes. - Per-agent MCP URLs move to the MCP host (release change, no chart change). MCP clients that used a per-agent MCP URL connect to
https://mcp.<DOMAIN>instead; per-agent MCP URLs are no longer served. The API’s own MCP endpoint,https://api.<DOMAIN>/mcp/(Model Context Protocol), does not change, so clients set up on it keep working. The chart has servedmcp.<DOMAIN>from its ingress since 0.22.0; there is no value to set.
xpander-postgres-upgrade-guard, refuses the upgrade when the running major version differs from the image’s, and lists every database. With postgres.upgradeGuard.backup.pvcName set, it first writes a pg_dumpall to that PVC, and refuses the upgrade if the dump cannot be taken in full. The dump must finish within postgres.upgradeGuard.timeoutSeconds (default 1500) and within the --timeout of the upgrade, so raise both for a large database. After the upgrade, the xpander-postgres-pgvector Job adds the vector extension to every database. It never fails the release, and its log ends with one status line:
helm rollback <release> <previous revision> returns the pod to postgres:15 on the same volume with all its data.
Coming from a chart before 0.22.0, these changes also apply:
-
The LLM Gateway runs. Before you upgrade, mirror the
llm-gwimage if you mirror images, make sure every provider your agents use has a key, and open any network path the gateway needs (LLM Gateway). agent-controller and api restart once to pick up the gateway’s address. After the upgrade, checkkubectl -n <namespace> get pods -l app=llm-gw(healthy result). -
Argo CD. Add
ignoreDifferenceson/dataforxpander-llm-gwandxpander-llm-gw-content, or setllm-gw.masterKey.existingSecretandllm-gw.contentKey.existingSecret. -
Harness agents run only on the fleet executors. There is no pod per agent; when no executor is up, a turn waits in the queue. The chart always keeps at least one executor:
fleet.replicas: 0(or blank) renders one, and withfleet.keda.enabled,fleet.keda.maxReplicas: 0holds one.agentContainers.harness(enabled,resources,storage,shm) is ignored and can be removed from your values;images.agentHarnessstays, it is the image every executor runs. -
The release Role reads pods but no longer writes them. agent-controller keeps get, list and watch on pods and still manages the Deployments, Services, volumes, Secrets and Jobs of agent workspaces. If your own automation used the release ServiceAccount to create, update or delete pods, add the rule back:
-
Fleet agents reach private networks like any other pod.
fleet.networkPolicy.exceptCidrsnow defaults to["169.254.0.0/16"]andexceptCidrsV6to["fe80::/10"], so agents and the MCP endpoints they call reach10/8,172.16/12,192.168/16and in-cluster services; cloud metadata stays blocked.--reset-then-reuse-valuespicks this up. With--reuse-values, pass--set-json 'fleet.networkPolicy.exceptCidrs=["169.254.0.0/16"]' --set-json 'fleet.networkPolicy.exceptCidrsV6=["fe80::/10"]'. An install that setexceptCidrsitself keeps its own list. -
Executor nodes need Landlock, or
fleet.landlock.enabled: false. See Node prerequisites. - Model key names and endpoints. Environment-variable key names that match the install’s own credentials read as no key, and model endpoints on metadata addresses are refused (details). Check the key names on your agents, custom providers and the organization’s default model before you upgrade.
- Custom-container agents move to the standard agent runtime during the upgrade.
--reuse-values with the new defaults:
- PostgreSQL no longer restarts on every chart release. The config checksum hashes the ConfigMap data only, so a release whose Postgres configuration is byte-identical leaves the StatefulSet alone; a real
postgres.*change still rolls it. - The fleet is always rendered, and
--atomicwaits for it. A running turn holds the roll for up to the drain window (fleet.executor.drainSeconds, 3600 s); the idle guard holds the executor at zero replicas on a cluster without its StorageClass so the upgrade completes anyway. If you drivehelm upgrade --waitby hand, give--timeoutat least the drain window or upgrade in a quiet period. Details on Sizing and requirements.
Installer Flags Reference
Automation & CI
The installer is built to be driven by scripts, CI pipelines, and coding agents:- Every prompt has a flag. With
--non-interactivenothing is read from the terminal, even when one exists: missing required values exit 2 with the usage and a completed rerun line rather than blocking. It implies--yes. - Stable progress output. Progress is one line per transition in a grep-friendly shape,
[3/6] chart-source: ok, and--quietreduces output to phase lines and the outcome only. Color is emitted only when stdout is a terminal. statusis the poll target. A read-only health report: edition, chart, release status, hook jobs, workload readiness, and URLs.--jsonemits one JSON object.
FIX:/RERUN: lines tell you what to repair. See Troubleshooting.
Advanced: Review-First / Manual Install
Read the installer before running it
- The versioned URL is immutable:
install-v<version>.shis published once per release and never rewritten in place, so pin it in runbooks and CI (bash install.sh --versionprints the version you’re holding). - The script is shellcheck-clean, enforced by CI on every push.
- Secrets never touch the helm command line: they are written to a temporary values file (mode 600, removed on exit), so they don’t land in shell history or process listings.
- Nothing runs against a cluster before the kube-context confirmation: the installer names the context and cluster it is about to install into and waits for your yes.
Do it yourself
Manual Helm install (no installer at all)
Manual Helm install (no installer at all)
helm upgrade --install; everything it does can be done by hand. The cleanest middle path is --values-out:https://deployment-manager.xpander.ai, or https://deployment-manager-privatelink.xpander.ai with PrivateLink) and install:secrets.static.* values. The full list is in the key mapping table. Common optional parameters:--set flags:Troubleshooting
Every installer failure prints two lines:FIX:, the action that repairs the condition, and RERUN:, your personalized non-interactive one-liner (secrets typed at prompts are shown as placeholders). The installer is idempotent: fix the condition, paste the rerun line, and it picks up where the failure left off. A secret-scrubbed session log is kept under ~/.xpander/logs/ (latest.log points at the most recent run). For scripted runs, the exit code names the failure class (Automation & CI), and bash install.sh status gives a read-only health verdict.
Installer
Release stuck in pending-install / pending-upgrade / failed
Release stuck in pending-install / pending-upgrade / failed
install.sh upgrade requires the release to be in deployed status: operating on a stuck release (pending-install, pending-upgrade, pending-rollback, failed, uninstalling) would strand it. Recover first:RERUN: line. A pending-install status usually means a first install was interrupted (Ctrl-C, lost connection) or timed out: helm uninstall is the right recovery for it.Release already runs the other deployment's chart
Release already runs the other deployment's chart
--release / --namespace), or remove the existing release first (helm uninstall xpander -n xpander).Piped run exits with 'these required flags are missing'
Piped run exits with 'these required flags are missing'
bash leaves no stdin to prompt on, so a piped run is non-interactive (the same contract as --non-interactive): Hybrid requires --org, --env, and --dm-api-key. Missing flags exit with code 2 and a completed rerun line. Copy the personalized one-liner from the xpander console, it carries the identity flags, or download the script and run bash install.sh for the interactive wizard.Cluster and Pods
Pods stuck in Init
Pods stuck in Init
agent-controller to be ready. Check its logs:Pods stuck in Pending
Pods stuck in Pending
AmazonEBSCSIDriverPolicy attached.exec format error
exec format error
t3, m5, c5, etc.).Insufficient CPU
Insufficient CPU
agent-worker pod requests 2 CPU by default. Two options:- Add more nodes, or use larger instances.
- For non-production environments only, lower the CPU request directly on the
xpander-agent-workerDeployment in thexpandernamespace:
Health checks failing
Health checks failing
agent-controller):PrivateLink
PrivateLink InvalidServiceName (cross-region)
PrivateLink InvalidServiceName (cross-region)
us-west-2, you must include --service-region us-west-2:--service-region, AWS looks for the service in your local region and fails with InvalidServiceName.PrivateLink connection timeout (HTTP 000)
PrivateLink connection timeout (HTTP 000)
Ingress and Networking
Ingress not accessible
Ingress not accessible
Load Balancer not provisioning
Load Balancer not provisioning
<pending>. Check its events (kubectl -n ingress-nginx describe svc ingress-nginx-controller):could not find any suitable subnets for creating the ELB: the VPC’s subnets are missing the ELB role tags. Tag public subnetskubernetes.io/role/elb=1for an internet-facing NLB; for an internal one, tag the private subnetskubernetes.io/role/internal-elb=1and annotate the Serviceservice.beta.kubernetes.io/aws-load-balancer-internal: "true".- On EKS Auto Mode, ensure the cluster role trust policy includes
sts:TagSession:
SSL certificate errors on chat URLs
SSL certificate errors on chat URLs
*.chat.<DOMAIN> as a subject alternative name. The chat UI generates per-thread subdomains (e.g., moccasin-prawn.chat.<DOMAIN>) that are not covered by *.<DOMAIN>.Request a new certificate with:API Keys
API key not being picked up after helm upgrade
API key not being picked up after helm upgrade
xpander-static secret has a Helm resource keep policy, so a helm upgrade does not update it on its own. Patch the key directly on the xpander-static Kubernetes secret in the xpander namespace:LLM calls fail with a missing API key
LLM calls fail with a missing API key
secrets.static), is refused with a message that names the missing key. Add the key in either place. To check the install’s keys, verify they reach the agent-worker pod:--set secrets.static.<key>=...), or check that your secret is correctly mounted through envFromSecretKeys: see Managing LLM API Keys.
