Skip to main content

tetrate agentrouter dataplane install

Install the complete Tetrate Agent Router data plane in a single command.


Synopsis​

tetrate agentrouter dataplane install <identity-file> [flags]

Description​

tetrate agentrouter dataplane install performs the full operator workflow:

  1. Load and validate the identity (service-account) file
  2. Optionally sync pinned images and the serve-helm OCI chart to a private registry (--image-sync)
  3. Optionally create a Kubernetes dockerconfigjson pull secret (--image-pull-secret-stdin)
  4. Generate Helm values
  5. helm upgrade --install the Agent Router data plane chart
  6. Optionally wait for pods to be ready (--wait)

helm and kubectl are downloaded automatically on first run into ~/.tetrate/tools/ (~/.tare/tools/ before 0.8.0) and reused on subsequent runs.

By default (--ha=true) the data plane Envoy proxy and its ExtProc sidecar deploy with HA-safe values: HPA minReplicas: 2, PDB minAvailable: 1, and EnvoyProxy.spec.shutdown.drainTimeout: 300s for graceful drain of in-flight LLM streams. If the target cluster has no metrics.k8s.io APIService (metrics-server absent), tetrate agentrouter dataplane install prints a non-fatal warning: the install-time floor of 2 still applies but the HPA cannot autoscale beyond it. Pass --ha=false for single-replica installs (lab / CI).

The egress extproc sidecar (ai-gateway-extproc) that the AI Gateway injects ships with a conservative default CPU request, so the HPA works out of the box. HPA utilization is computed across every container in the egress pod, and a container with no CPU request leaves the HPA unable to compute a value (FailedGetResourceMetric: missing request for cpu in container ai-gateway-extproc). The request is part of the HPA's aggregate denominator (the target percentage is measured against the summed envoy + extproc requests), so if the default does not match the observed traffic, override it in the data plane Helm values (tetrate agentrouter dataplane install / tetrate agentrouter dataplane upgrade --helm-values <file>):

egressResources:
egress:
extProc:
resources:
requests:
cpu: 250m # size to observed extproc CPU (kubectl top pod --containers)
memory: 256Mi
limits:
memory: 512Mi

Set this in the persistent values, not with a live kubectl edit: the egress objects are reconciled and an in-cluster edit is overwritten. No CPU limit is set by default (a CPU limit would throttle the sidecar); the memory limit guards OOM.

Size these to the cluster

The shipped values are a conservative starting point. Under representative load, watch the sidecar with kubectl top pod -n tars-dataplane --containers and adjust for the cluster:

  • Raise limits.memory if extproc approaches it: the memory limit is a hard cap, so an under-sized value causes OOM kills under heavy or large-payload traffic.
  • Size requests.cpu to real usage, because it directly shifts the HPA's scale-out threshold.

Node size, request volume, and payload size all affect the right numbers.

Applying the request when upgrading an existing data plane

Fresh installs pick up the request automatically. On an upgrade, the running egress pods keep their old request-less sidecar until they restart (the request is injected only at pod admission), so the HPA stays FailedGetResourceMetric until the deployment is rolled:

kubectl rollout restart deployment/egress -n tars-dataplane

When upgrading from a version that predates the GatewayConfig CRD (gatewayconfigs.aigateway.envoyproxy.io), apply CRDs first. tetrate agentrouter dataplane upgrade does not reapply them, so helm upgrade otherwise fails with no matches for kind "GatewayConfig":

tetrate agentrouter dataplane install --crds-only # then run your upgrade
--serve-url is deprecated

--serve-url is still accepted, prints a deprecation notice, and is otherwise ignored, so existing scripts keep working. Since 0.7.0 the hostnames a data plane serves are declared at install with --gateway-hostname, described below. The deprecation notice the CLI prints still says to set the URL from the management plane; on a data plane on 0.7.0 or later, declare the hostnames instead.

Usage​

Basic install​

tetrate agentrouter dataplane install identity.json

Deploys using the embedded Helm chart with defaults:

SettingDefault
Release nametars
System namespacetars-system
Dataplane namespacetars-dataplane
Customerinferred from identity file

Without --gateway-hostname the install declares no hostname, and the data plane keeps the address stored for it on the management plane, if one was stored before 0.7.0. See Declare the hostnames to give a new data plane its customer-facing names.

Declare the hostnames​

Pass each hostname the data plane serves with --gateway-hostname. The flag is repeatable, an entry may carry a port, and the first entry is the address the Admin Console and the Developer Console print wherever they show one address:

tetrate agentrouter dataplane install identity.json \
--gateway-hostname=eu.acme.example.com \
--gateway-hostname=dp.acme.example.com:8443

The flag renders global.gateway.hostnames in the serve-helm values. The data plane reports the list to the management plane on its next configuration poll, and the management plane shows it read-only on the data plane's page. Declaring a name creates no DNS record, certificate, or load balancer route; those stay the operator's. --gateway-hostname="" on its own declares that the data plane serves no hostname, which drains it. The full rules are in Declare the hostnames a data plane serves.

Print the generated Helm values to stdout without touching the cluster:

tetrate agentrouter dataplane install identity.json --print-helm-values

Useful for inspecting values or piping into GitOps tooling. --dry-run is accepted as a hidden alias.

Image sync​

Sync all pinned images and the serve-helm OCI chart from the source registry to a private registry before installing:

tetrate agentrouter dataplane install identity.json \
--image-sync myregistry.example.com
  • The SA JWT token is used to authenticate to the source registry.
  • Since 0.7.0 the sync copies every image the selected chart pulls, at the tag the chart deploys, including the ones a release before 0.7.0 left out (oauthrefresh, pgash-sampler, the catalog-sync tare image, and berglas-kubectl), so a registry populated only by --image-sync no longer leaves workloads in ImagePullBackOff. With --chart-version, the tags are read from that chart rather than from the CLI's own build, so mirroring, the upgrade precheck, and the deploy all name the same tag.
  • After the sync, --image-registry (the Helm global.imageRegistry) points at the sync destination so the cluster pulls from where the images now live.
  • Source registry priority: --image-registry (when --image-sync is also set) → REGISTRY_URL env → registry.tetrate.ai (default).
  • The serve-helm chart is mirrored as an OCI artifact alongside the images (for example myregistry.example.com/serve-helm:0.7.0). Since 0.7.0 a chart prerelease reads 0.7.0-rc.1.gabc1234, with the commit in a prerelease field, so one string is valid as both a SemVer version and an OCI tag; earlier charts carried the commit as + build metadata, which OCI tags forbid, and were stored as 0.6.0-rc.3_8d71fcc. Both forms can still be pinned. The counter lives in the prerelease segment (rc.4, alpha.347) so versions are orderable; tetrate agentrouter versions list --channel rc finds the newest release candidate. tetrate agentrouter dataplane install itself still uses the chart embedded in the CLI binary; the mirrored chart exists so downstream helm upgrade oci://myregistry.example.com/serve-helm workflows succeed without reaching back to registry.tetrate.ai.
  • For release-tagged tetrate builds the chart also gets an extra tag equal to the running CLI version (e.g. myregistry.example.com/serve-helm:v1.2.3) so a downstream helm install --version <tare-version> resolves to the chart that shipped with that CLI. Dev / dirty builds skip the extra tag to keep the mirror free of ambiguous tags.

Sync only (no deploy):

tetrate agentrouter dataplane install identity.json \
--image-sync myregistry.example.com --sync-only

Mirror-to-mirror sync: pull from an existing private registry and push to a new one in a single invocation. When paired with --image-sync, --image-registry is interpreted as the source registry to pull from; without --image-sync it keeps its original meaning of "set the Helm global.imageRegistry".

tetrate agentrouter dataplane install identity.json \
--image-registry acme.registry.example.com \
--image-sync acme1.registry.example.com \
--sync-only

Print the image list without copying:

# Just the source references
tetrate agentrouter dataplane install identity.json --print-images

# Source → destination pairs (requires --image-sync)
tetrate agentrouter dataplane install identity.json \
--image-sync myregistry.example.com --print-images

Apply CRDs only​

Apply the chart's CRDs to the cluster and exit before namespaces, pull secret, and Helm install. Useful for three operational patterns:

  • Cluster-admin handoff: a privileged operator pre-applies CRDs (which are cluster-scoped), then a less-privileged operator runs the rest of tetrate agentrouter dataplane install against a namespace they own.
  • Race-free upgrades: apply CRDs first so subsequent helm upgrade runs render against the new schema with no chicken-and-egg ordering.
  • Raw helm install workflows: operators who drive Helm directly (GitOps, Argo CD, custom CI) hand the CRD lifecycle to tetrate and let Helm own only the workload resources.
tetrate agentrouter dataplane install identity.json --crds-only

After the CRDs land, re-run without --crds-only to complete the install. --crds-only cannot be combined with --sync-only, --image-sync, --image-pull-secret-stdin, or any --print-* flag.

Followed by a raw helm install​

To skip tetrate agentrouter dataplane install for the workload step and run Helm directly, Helm must be told not to render or adopt the CRDs that tetrate already applied via server-side apply. Two flags are needed:

# Step 1: apply CRDs with tetrate (kubectl server-side apply; no Helm ownership labels)
tetrate agentrouter dataplane install identity.json --crds-only

# Step 2: install the workload with helm, skipping CRDs in both places they live
helm install tars oci://<registry>/serve-helm \
--version "<version>" \
-f values.yaml -n tars-system \
--skip-crds \
--set ai-gateway-crds.enabled=false
  • --skip-crds tells Helm to skip CRDs in the chart's crds/ directory.
  • --set ai-gateway-crds.enabled=false disables the ai-gateway-crds subchart, which renders CRDs as templates (not as crds/ files) and would otherwise fail Helm's ownership-adoption check against the kubectl-applied CRDs.

Without both flags, Helm errors with:

CustomResourceDefinition "aigatewayroutes.aigateway.envoyproxy.io" ...
exists and cannot be imported into the current release: invalid ownership
metadata; label validation error: missing key "app.kubernetes.io/managed-by"

This split (tetrate owns CRDs, Helm owns the workload) is the recommended pattern for GitOps and raw-Helm consumers. CRDs survive helm uninstall tars because Helm does not manage them, so customer data (Gateways, AIGatewayRoutes, MCPRoutes, …) is preserved.

Pull secret​

Create a Kubernetes dockerconfigjson secret from credentials read on stdin:

echo "user:password" | tetrate agentrouter dataplane install identity.json \
--image-sync myregistry.example.com \
--image-pull-secret-stdin
  • The secret is created (or updated) in both tars-system and tars-dataplane namespaces.
  • Re-runs are idempotent (kubectl apply under the hood).
  • Default secret name: tars-image-pull-secret. Override with --image-pull-secret-name.

To reference a pre-existing secret without creating one:

tetrate agentrouter dataplane install identity.json \
--image-pull-secret-name my-existing-secret

Full workflow​

echo "user:password" | tetrate agentrouter dataplane install identity.json \
--image-sync myregistry.example.com \
--image-pull-secret-stdin \
--wait

With guardrails​

Include llm-guard-api and envoy-dm images and enable guardrails configuration:

echo "user:password" | tetrate agentrouter dataplane install identity.json \
--image-sync myregistry.example.com \
--image-pull-secret-stdin \
--enable-guardrails \
--wait

The guardrails container (llm-guard-api) requires a node with sufficient memory and CPU. Use --guardrails-config to pin it to the right node pool:

# guardrails.yaml
# nodeSelector:
# node-type: gpu-large
#
# tolerations:
# - key: large-node
# operator: Exists
# effect: NoSchedule

echo "user:password" | tetrate agentrouter dataplane install identity.json \
--image-sync myregistry.example.com \
--image-pull-secret-stdin \
--enable-guardrails \
--guardrails-config guardrails.yaml \
--wait

The config file uses natural Kubernetes YAML rather than escaped JSON strings:

# guardrails.yaml
nodeSelector:
node-type: gpu-large

tolerations:
- key: large-node
operator: Exists
effect: NoSchedule

# Optional: full affinity rules
affinity:
nodeAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
nodeSelectorTerms:
- matchExpressions:
- key: cloud.google.com/gke-nodepool
operator: In
values: [llm-guard-pool]

Pass --guardrails-config - to read the config from stdin instead of a file (follows the kubectl/helm convention):

cat <<'EOF' | tetrate agentrouter dataplane install identity.json \
--enable-guardrails \
--guardrails-config - \
--wait
nodeSelector:
node-type: gpu-large
tolerations:
- key: large-node
operator: Exists
effect: NoSchedule
EOF
note

--guardrails-config - and --image-pull-secret-stdin both read stdin; use a file when combining both.

Print namespace and pull-secret manifests without touching the cluster:

echo "user:password" | tetrate agentrouter dataplane install identity.json \
--image-sync myregistry.example.com \
--image-pull-secret-stdin \
--print-resources

Pipe directly into kubectl apply or commit to a GitOps repo.

Post-renderer (shared clusters)​

Since 0.3.0, --post-renderer names an executable that Helm pipes the rendered manifest through before applying it:

tetrate agentrouter dataplane install identity.json --post-renderer ./rename-cluster-scoped.sh

The charts name a few cluster-scoped RBAC objects fixedly, so a second install into the same cluster is refused with exists and cannot be imported into the current release. When several data planes share one cluster, a post-renderer is the place to give those objects distinct names. Helm records what the renderer produced, so helm uninstall still removes it. The same executable is used for --print-resources and for the server-side dry run, so all three agree on what would be applied.

Flags​

Main​

FlagDefaultDescription
--gateway-hostname <host[:port]>noneSince 0.7.0. A hostname this data plane serves, rendered under global.gateway.hostnames. Repeatable; the first is the address the consoles print. Left out, the data plane keeps the address the management plane stores for it. --gateway-hostname="" alone declares no hostname.
--serve-url <URL>deprecatedDeprecated since 0.3.0: accepted, prints a notice, and is ignored. Declare the hostnames with --gateway-hostname.
--hatrueHA-safe defaults for the data-plane Envoy proxy: HPA minReplicas: 2, PDB minAvailable: 1. Pass --ha=false for single-replica lab/CI installs.
--drain-timeout-seconds300EnvoyProxy.spec.shutdown.drainTimeout (seconds). Maximum time Envoy waits for in-flight LLM streams to finish before SIGKILL. The Envoy Gateway controller propagates this into the data-plane Pod's terminationGracePeriodSeconds.
--enable-guardrailsfalseInclude guardrails images and configuration
--guardrails-config <file|->noneYAML file with Guardrails pod scheduling constraints (nodeSelector, affinity, tolerations), or - to read from stdin; requires --enable-guardrails
--waitfalseWait for pods ready after deploy
--post-renderer <path>noneExecutable Helm pipes the rendered manifest through before applying it; also used by --print-resources and the server-side dry run. See Post-renderer

Proxy​

Interactive prompts ask for these when none is passed and stdin is a terminal; passing any of the first three suppresses the prompts. The two egress paths, both topologies, and worked examples are in Route outbound traffic through a corporate proxy.

FlagDefaultDescription
--http-proxy <URL>noneHTTP_PROXY for the controller, controller-worker, and data-plane Envoy pods; also the value the LLM forward proxy is derived from
--https-proxy <URL>noneHTTPS_PROXY for the same pods. Not defaulted from --http-proxy; pass both
--no-proxy <list>noneNO_PROXY for the same pods: comma-separated hostnames, domain suffixes, and CIDRs
--forward-proxy-address <host:port>derived from --http-proxyEnvoy's LLM-egress forward proxy (EGRESS_FORWARD_PROXY_ADDRESS on the ai-gateway-controller). Pass an explicit empty value (--forward-proxy-address "") to disable the tunnel while keeping the management-traffic proxy
--forward-proxy-no-proxy <list>noneHostname suffixes that skip the LLM forward proxy (no CIDRs); only effective while the forward proxy is active

Registry​

FlagDefaultDescription
--image-sync <REG>noneSync images and the serve-helm OCI chart to <REG> before install; sets --image-registry to <REG> after the sync (unless --image-registry was used to override the source)
--image-pull-secret-stdinfalseRead user:password from stdin and create a pull secret
--image-pull-secret-name <NAME>tars-image-pull-secretName of the pull secret to create or reference
--sync-onlyfalseSync images then exit without deploying (requires --image-sync)

Inspect​

FlagDefaultDescription
--print-helm-valuesfalsePrint generated Helm values to stdout without deploying
--print-resourcesfalsePrint Kubernetes manifests (namespaces, pull secret) to stdout without applying
--print-imagesfalsePrint image list and exit (add --image-sync for src→dst pairs)
--crds-onlyfalseApply CRDs to the cluster and exit; skip namespaces, pull secret, Helm install, and wait. Incompatible with --sync-only, --image-sync, --image-pull-secret-stdin, and any --print-* flag.

Advanced / hidden​

These flags are hidden from --help but accepted for advanced use.

FlagDefaultDescription
--dry-runfalseAlias for --print-helm-values
--release-nametarsHelm release name
--system-namespacetars-systemKubernetes system namespace
--dataplane-namespacetars-dataplaneKubernetes dataplane namespace
--chart-pathembeddedPath or OCI/HTTP reference to Helm chart
--chart-versionnoneChart version (required for remote/OCI charts)
--image-registrynoneContainer image registry. Without --image-sync: sets Helm global.imageRegistry. With --image-sync: source registry to pull from (push destination is --image-sync). Falls back to REGISTRY_URL env, then registry.tetrate.ai
--image-tagnoneImage tag override
--helm-valuesnoneAdditional Helm values file to merge
--deployment-modeenterpriseenterprise or saas
--environmentnoneEnvironment label
--customerfrom identityCustomer label override
--redis-typeclustercluster or hosted
--rate-limitingtrueEnable rate limiting
--timeout5mTimeout for --wait
--guardrails-node-selectornoneNode selector for Guardrails pods as key=value pairs (repeatable); prefer --guardrails-config
--guardrails-affinitynoneAffinity rules for Guardrails pods as a JSON object; prefer --guardrails-config
--guardrails-tolerationsnoneTolerations for Guardrails pods as a JSON array; prefer --guardrails-config

Examples​

Production deployment with private registry​

# 1. Sync images (run once per release upgrade)
tetrate agentrouter dataplane install identity.json \
--image-sync gcr.io/acme/tars \
--sync-only

# 2. Install (or upgrade)
echo "user:password" | tetrate agentrouter dataplane install identity.json \
--image-sync gcr.io/acme/tars \
--image-pull-secret-stdin \
--release-name acme-serve \
--wait

GitOps: generate values only​

tetrate agentrouter dataplane install identity.json \
--image-registry gcr.io/acme/tars \
--image-pull-secret-name acme-pull-secret \
--guardrails-config guardrails.yaml \
--enable-guardrails \
--print-helm-values > helm/tars-values.yaml

Commit helm/tars-values.yaml and apply separately:

helm upgrade --install tars charts/valet/serve \
--namespace tars-system --create-namespace \
-f helm/tars-values.yaml

Preview images before syncing​

tetrate agentrouter dataplane install identity.json \
--image-sync myregistry.example.com \
--enable-guardrails \
--print-images

Output, abbreviated. Since 0.7.0 an image built with the release carries the chart's version as its tag, a patched upstream image carries a release-qualified tag suffixed with the upstream build's own tag, and a mirrored upstream image keeps the upstream tag:

registry.tetrate.ai/liaison:0.7.0 → myregistry.example.com/liaison:0.7.0
registry.tetrate.ai/tare-doctor:0.7.0 → myregistry.example.com/tare-doctor:0.7.0
registry.tetrate.ai/gateway:0.7.0-egw.v1.7.4.de92dcb → myregistry.example.com/gateway:0.7.0-egw.v1.7.4.de92dcb
registry.tetrate.ai/redis:<upstream tag> → myregistry.example.com/redis:<upstream tag>
...
registry.tetrate.ai/serve-helm:0.7.0 → myregistry.example.com/serve-helm:0.7.0

The exact list and tags come from the running CLI and the selected chart, which is why the ECR path in the AWS installation guide reads them from --print-images rather than from a hand-kept list.

Output​

All progress is written to stderr; only --print-helm-values, --print-images, and --print-resources write to stdout, so they are safe to redirect:

Syncing images registry.tetrate.ai → myregistry.example.com
Copying registry.tetrate.ai/gateway:0.7.0-egw.v1.7.4.de92dcb → myregistry.example.com/gateway:0.7.0-egw.v1.7.4.de92dcb
2009/11/10 23:00:00 existing blob: sha256:abc123...
...
Image sync complete.

Creating pull secret "tars-image-pull-secret" in namespace tars-system...
pull secret "tars-image-pull-secret" ok
Creating pull secret "tars-image-pull-secret" in namespace tars-dataplane...
pull secret "tars-image-pull-secret" ok

Installing Agent Router dataplane...
release: tars
system namespace: tars-system
dataplane namespace: tars-dataplane

Creating Kubernetes namespaces...
namespace tars-system: ok
namespace tars-dataplane: ok

Deploying with Helm...
Helm deployment complete

Installation complete.

Next steps:
kubectl get pods -n tars-system
kubectl logs -n tars-system -l app=tars

Verification​

kubectl get pods -n tars-system
kubectl get pods -n tars-dataplane
kubectl logs -n tars-system -l app=tars

Management-plane event reporting​

After the install completes, the CLI writes a ConfigMap labeled tars.io/component=install-event to tars-system. The tare-doctor CronJob (deployed with this chart) forwards that ConfigMap to the management plane on its next tick (default: every 5 minutes) so the data plane's deployment history reflects the new install. On a successful forward the ConfigMap is deleted; on a 5xx / network failure it is retained for the next tick; on a 4xx it is dropped (the same payload would re-reject).

If the chart is installed without going through the tetrate CLI (raw helm install, ArgoCD / Flux, customer GitOps), the same Admin Console row appears via the tare-doctor reconciler, which observes the helm release secret and emits an event with source: tare-doctor-poll. Operator attribution is empty on the non-CLI path (rendered as a dash in the timeline).

Rollback and uninstall​

# View history
helm history tars -n tars-system

# Rollback
helm rollback tars -n tars-system

# Uninstall
helm uninstall tars -n tars-system
kubectl delete namespace tars-system tars-dataplane

Troubleshooting​

Error: failed to register data plane URL​

[5/6] Register data plane URL
Error: failed to register data plane URL: management plane SetDataplaneURL failed:
permission_denied: insufficient permissions: requires scopes [dataplane_operator]

The install was run with --serve-url on a CLI older than the flag's deprecation. Registering the gateway URL is a management-plane action, and a data-plane service account deliberately lacks the dataplane_operator scope the call requires, so the management plane refuses it. The failure lands before Helm runs: nothing has been deployed and nothing is broken.

Run the install again without --serve-url, or upgrade the CLI, on which the flag is ignored. On a 0.7.0 or later CLI, pass the hostnames with --gateway-hostname instead.