> ## Documentation Index
> Fetch the complete documentation index at: https://docs.xpander.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# Install Hybrid

> Install xpander Hybrid with the one-liner from xpander Chat, follow the wizard, and verify the connection.

Install Hybrid from a terminal with `kubectl` access to your cluster. Check [Requirements](https://docs.xpander.ai/self-hosted/hybrid.md#requirements) first.

| Step | What you do | Result |
| - | - | - |
| [1. Get your one-liner](#1-get-your-one-liner-from-xpander-chat) | Add a Hybrid location in xpander Chat and copy the command | A command that carries your identity |
| [2. Run the installer](#2-run-the-installer) | Paste it in a terminal with `kubectl` access and answer the wizard | Six install phases, each with a `FIX:` and `RERUN:` line on failure |
| [3. Verify the connection](#3-verify-the-connection) | Check the pods and the console | **"Connection established"** in a minute or two |

## 1. Get your one-liner from xpander Chat

1. In xpander Chat, open **Platform > Agents > Runtime** and choose the **Locations** tab (Connected runtimes and regions)
2. Click **"Add Location"**
3. Choose the **Hybrid** edition. The dialog shows the minimum footprint for the deployment you picked.
4. 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)
5. Copy the generated one-liner

The one-liner carries your identity, `--org`, `--env`, and `--dm-api-key` (plus `--domain` when you set a URL). That is how the cluster authenticates to the xpander control plane:

```bash theme={"dark"}
curl -fsSL https://charts.xpander.ai/install.sh | bash -s -- \
  --edition hybrid \
  --org <ORGANIZATION_UUID> \
  --env <ENVIRONMENT_UUID> \
  --dm-api-key <DEPLOYMENT_MANAGER_API_KEY> \
  --domain xpander.my-company.com
```

> [!NOTE]
>
> The one-liner deliberately carries **no LLM provider credentials**: the wizard prompts for those interactively (input hidden), or you pass them as flags. Secrets are written to a temporary values file (mode 600, removed on exit) and never appear on the helm command line.

## 2. Run the installer

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.

**1. Edition & requirements**

If `--edition` wasn't given, it asks. It then prints the minimum footprint for that edition (the same numbers as [charts.xpander.ai/requirements.json](https://charts.xpander.ai/requirements.json)) and asks you to confirm your cluster meets it, stopping cleanly if not.

**2. Identity**

Organization ID, Environment ID, and the Deployment Manager API key (input hidden): all from the install card in xpander Chat, all skipped if passed as flags.

**3. Ingress domain (optional)**

Press Enter to skip and keep cluster-internal Services (port-forward reaches them, add ingress later). With a domain, choose the hostname style: **subdomain** style (`agent-controller.xpander.<DOMAIN>`, needs wildcard DNS) or **dash** style (`agent-controller-xpander.<DOMAIN>`, individual A records). See [External access and DNS](https://docs.xpander.ai/self-hosted/hybrid-external-access.md).

**4. Control plane reachability**

Public internet (`https://deployment-manager.xpander.ai`), AWS PrivateLink (`https://deployment-manager-privatelink.xpander.ai`, [set it up first](https://docs.xpander.ai/self-hosted/hybrid-privatelink.md)), 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.

**5. LLM providers (all optional)**

OpenAI, Anthropic, Google/Gemini, Amazon Bedrock, then optionally Fireworks, Nebius, Helicone and OpenRouter. All skippable: xpander starts without keys, and model calls work once you add a key in the UI or with a helm upgrade.

For **Bedrock** it asks how to authenticate: an API key / bearer token (`AWS_BEARER_TOKEN_BEDROCK`), or keyless IAM role / EKS pod identity. See [Amazon Bedrock: keyless IAM or API key](https://docs.xpander.ai/self-hosted/hybrid-llm-keys.md#amazon-bedrock-keyless-iam-or-api-key).

**6. Namespace, context confirmation, summary**

Namespace (default `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 and CI](https://docs.xpander.ai/self-hosted/hybrid-upgrade.md#automation-and-ci)):

```
[1/6] preflight       helm ≥ 3.13, kubectl, cluster reachable, Kubernetes ≥ 1.28
[2/6] configuration   context confirmation + masked summary
[3/6] chart-source    helm repo add xpander https://charts.xpander.ai (or --chart-path)
[4/6] values          secrets written to a mode-600 temp file, removed on exit
[5/6] install         helm upgrade --install xpander xpander/xpander
[6/6] verify          waits for the agent-controller rollout (5-minute timeout)
```

The preflight accepts Helm 3.13, but the documented upgrade command (`--reset-then-reuse-values`) needs Helm 3.14 or later.

Every failure prints `FIX:` (the repair) and `RERUN:` (your non-interactive one-liner, secrets placeholdered). The installer is idempotent. See [Troubleshooting](https://docs.xpander.ai/self-hosted/hybrid-troubleshooting.md).

## 3. Verify the connection

The installer prints a summary. Within a minute or two the console shows **"Connection established"**.

```bash theme={"dark"}
kubectl get pods -n xpander
```

Expect one `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:

```bash theme={"dark"}
kubectl -n xpander port-forward service/xpander-agent-controller 9016:9016 &
kubectl -n xpander port-forward service/xpander-ai-gateway 9018:9018 &
kubectl -n xpander port-forward service/xpander-mcp 8081:8081 &

curl http://localhost:9016/health
curl http://localhost:9018/health
curl http://localhost:8081/health
```

The installation then appears in the **ENVIRONMENTS** switcher and under **Platform > Agents > Runtime** (**Locations** tab), where **Components** shows heartbeats per service and **Configuration** holds the Chat, Streaming and Scheduler controls.

## Advanced: review-first or manual install

Read the installer before running it:

```bash theme={"dark"}
curl -fsSL https://charts.xpander.ai/install.sh | less
curl -fsSL https://charts.xpander.ai/install-v<version>.sh | less   # this exact version
```

> [accordion] What the installer guarantees
>
> * The **versioned URL is immutable**: `install-v<version>.sh` is published once per release and never rewritten in place, so pin it in runbooks and CI (`bash install.sh --version` prints 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.

To do it yourself:

| Path | What you get |
| - | - |
| `--manual` | The whole install as copy-pasteable commands (repo setup, secrets, a complete values file, the helm command, verification) personalized from the flags you passed, **executing nothing**. Secrets stay placeholders |
| `--values-out PATH` | Just the generated values file (mode 600), plus the printed `helm upgrade --install` command to run yourself. No cluster needed |
| `--dry-run` | The full flow rendered through `helm --dry-run`, installing nothing |

```bash theme={"dark"}
bash install.sh --edition hybrid --org <ORG> --env <ENV> --dm-api-key <KEY> --manual
```

> [accordion] Manual Helm install (no installer at all)
>
> The installer is a wrapper around `helm upgrade --install`. Everything it does can be done by hand. The cleanest middle path is `--values-out`:
>
> ```bash theme={"dark"}
> bash install.sh --edition hybrid --org <ORG> --env <ENV> --dm-api-key <KEY> \
>   --domain <DOMAIN> --values-out xpander-values.yaml
> helm upgrade --install xpander xpander/xpander \
>   --namespace xpander --create-namespace -f xpander-values.yaml
> ```
>
> Or fully by hand. Add the repository:
>
> ```bash theme={"dark"}
> helm repo add xpander https://charts.xpander.ai
> helm repo update
> ```
>
> Set the deployment manager URL based on your connectivity choice (public `https://deployment-manager.xpander.ai`, or `https://deployment-manager-privatelink.xpander.ai` with [PrivateLink](https://docs.xpander.ai/self-hosted/hybrid-privatelink.md)) and install:
>
> ```bash theme={"dark"}
> helm upgrade --install xpander xpander/xpander \
>   --namespace xpander --create-namespace \
>   --set ingress.enabled=true \
>   --set domain=<DOMAIN> \
>   --set global.organizationId=<ORGANIZATION_ID> \
>   --set global.environmentId=<ENVIRONMENT_ID> \
>   --set secrets.static.deploymentManagerApiKey=<DEPLOYMENT_MANAGER_API_KEY> \
>   --set urls.deploymentManager=<DEPLOYMENT_MANAGER_URL> \
>   --timeout 10m
> ```
>
> LLM provider keys are `secrets.static.*` values. The full list is in the [key mapping table](https://docs.xpander.ai/self-hosted/hybrid-llm-keys.md#secret-field-names). Common optional parameters:
>
> | Parameter | Default | Description |
> | - | - | - |
> | `domain` | `""` (ingress disabled) | Base domain for ingress hostnames |
> | `ingress.enabled` | `false` | Expose services via ingress |
> | `ingress.tls.enabled` | `false` | Enable TLS on ingress |
> | `ingress.tls.source` | `self-signed` | `self-signed`, `cert-manager`, or `external` |
> | `redis.storage.size` | `8Gi` | Redis PVC size |
> | `redis.storage.storageClass` | cluster default | Storage class for Redis PVC |
> | `dockerRegistry.enabled` | `true` | In-cluster registry for agent container images. Disable it if your cluster provides one |
> | `metricsServer.enabled` | `true` | metrics-server for autoscaling. Disable it if your cluster runs one |
>
> For production, use a values file instead of `--set` flags:
>
> ```yaml xpander-values.yaml theme={"dark"}
> domain: "xpander.production.com"
>
> global:
>   organizationId: "your-org-id"
>   environmentId: "your-env-id"
>   env:
>     LOG_LEVEL: "info"
>     ENVIRONMENT: "production"
>
> urls:
>   deploymentManager: "https://deployment-manager.xpander.ai"
>
> secrets:
>   static:
>     deploymentManagerApiKey: "your-key"
>     agentsOpenaiApiKey: "sk-your-openai-key"
>     anthropicApiKey: "sk-ant-your-anthropic-key"
>
> ingress:
>   enabled: true
>   tls:
>     enabled: true
>     source: "cert-manager"
>   annotations:
>     cert-manager.io/cluster-issuer: "letsencrypt-prod"
>     nginx.ingress.kubernetes.io/ssl-redirect: "true"
>
> resources:
>   agentController:
>     requests:
>       cpu: "1000m"
>       memory: "2Gi"
>     limits:
>       cpu: "3000m"
>       memory: "4Gi"
>
> redis:
>   storage:
>     size: "32Gi"
>     storageClass: "fast-ssd"
> ```
>
> ```bash theme={"dark"}
> helm upgrade --install xpander xpander/xpander \
>   --namespace xpander --create-namespace \
>   --values xpander-values.yaml
> ```
>
> See [Sizing and requirements](https://docs.xpander.ai/self-hosted/sizing.md) for recommended resource values per service.

## Installer flags reference

```bash theme={"dark"}
bash install.sh [flags]            # install (the wizard, then helm upgrade --install)
bash install.sh upgrade [flags]    # upgrade an existing release in place
bash install.sh status [flags]     # read-only health report (--json for one object)
```

Air-gap-only flags (license, mirroring, image tags, data tier, base domain, code execution): [Air-gapped deployment](https://docs.xpander.ai/self-hosted/airgap.md).

> [accordion] Identity
>
> Hybrid identifies with the three values shown in xpander Chat. Required non-interactively.
>
> | Flag | Description |
> | - | - |
> | `--edition hybrid\|airgap` | Edition. `hybrid` (default): execution tier here, control plane in xpander cloud. `airgap`: every component here, no connection to xpander cloud at run time |
> | `--org UUID` | Organization ID |
> | `--env UUID` | Environment ID |
> | `--dm-api-key KEY` | Deployment manager API key |

> [accordion] LLM providers
>
> All optional, the wizard asks for these.
>
> | Flag | Description |
> | - | - |
> | `--openai-key KEY` | OpenAI |
> | `--anthropic-key KEY` | Anthropic |
> | `--google-key KEY` | Google / Gemini |
> | `--bedrock-key KEY` | Bedrock bearer token (`AWS_BEARER_TOKEN_BEDROCK`) |
> | `--bedrock-auth key\|iam` | `key` (default when `--bedrock-key` is given) or keyless IAM role / EKS pod identity, see [Amazon Bedrock: keyless IAM or API key](https://docs.xpander.ai/self-hosted/hybrid-llm-keys.md#amazon-bedrock-keyless-iam-or-api-key) |
> | `--fireworks-key KEY` | Fireworks AI |
> | `--nebius-key KEY` | Nebius |
> | `--helicone-key KEY` | Helicone observability |
> | `--openrouter-key KEY` | OpenRouter |

> [accordion] Hybrid connectivity & ingress
>
> | Flag | Description |
> | - | - |
> | `--domain DOMAIN` | Enable ingress for this domain |
> | `--dash-domains` | Dash-separated hostnames (`agent-controller-xpander.<DOMAIN>`) |
> | `--privatelink` | Reach the deployment manager over [AWS PrivateLink](https://docs.xpander.ai/self-hosted/hybrid-privatelink.md) |
> | `--staging` | Point at the xpander staging control plane (auto-detected for local/staging domains and local kube contexts) |
> | `--dm-url URL` | Custom deployment manager URL. `--privatelink` / `--staging` / `--dm-url` are mutually exclusive |

> [accordion] Chart and release
>
> | Flag | Description |
> | - | - |
> | `--chart-path PATH` | Install from a local chart directory or `.tgz` |
> | `--chart-version VERSION` | Pin the chart version |
> | `--set KEY=VALUE` | Extra helm `--set` (repeatable) |
> | `--values FILE` | Extra helm `-f` values file (repeatable) |
> | `--namespace NAME` | Default: `xpander` |
> | `--release NAME` | Default: `xpander` |
> | `--kube-context NAME` | kubectl/helm context |

> [accordion] Other
>
> | Flag | Description |
> | - | - |
> | `--manual` | Print the whole install as copy-pasteable commands, personalized from the flags you passed, and do nothing. Both self-hosted deployments. Secrets stay placeholders. See [Advanced](#advanced-review-first-or-manual-install) |
> | `--values-out PATH` | Write the generated values file to PATH and exit: no cluster needed. Useful for GitOps: review the file, then `helm upgrade --install` it yourself |
> | `--dry-run` | Render everything through helm, install nothing |
> | `-y`, `--yes` | Accept every confirmation |
> | `--non-interactive` | Never read the terminal, even if one exists. Implies `--yes`. Missing required flags exit 2 instead of prompting |
> | `-q`, `--quiet` | Phase lines and the outcome only |
> | `--json` | `status` only: emit one JSON object |
> | `--help` / `--version` | Usage text / installer version |

## Next steps

> [card] [Let users reach the cluster](https://docs.xpander.ai/self-hosted/hybrid-external-access.md)
>
> VPN, or ingress, NLB and DNS

> [card] [Add model keys](https://docs.xpander.ai/self-hosted/hybrid-llm-keys.md)
>
> Provider keys and Bedrock


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.