upgrade subcommand:
Upgrading
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:
What each change needs (0.22.1 to 0.22.5)
What each change needs (0.22.1 to 0.22.5)
- 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.
The Postgres upgrade, step by step
The Postgres upgrade, step by step
A pre-upgrade Job, To go back,
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
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 Runtime and node reference.
Automation and 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. All flags: Installer flags reference. - 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.
Exit codes
Exit codes

