Agent Router data plane installation using Helm
This guide is for platform teams that own the Helm release lifecycle and already have a private container registry, such as Azure Container Registry, Harbor, JFrog Artifactory, Nexus, or Google Artifact Registry.
In this guide:
taresyncs TARE release artifacts into the private registry.tareapplies CRDs outside Helm.tarerenders Helm values.- Helm installs or upgrades the data plane workload from the private registry.
Plan for 30--60 minutes, plus the time to mirror artifacts into the private registry.
This guide covers data plane installation only. It does not create a public Gateway, ingress, DNS record, or TLS certificate. For gateway exposure, follow the published cloud install guide for the target platform, or use the organization's preferred ingress or Gateway stack.
Architecture overview
The Agent Router platform uses a split-plane model. A Management Plane hosted by Tetrate holds configuration and serves the web UI. A Data Plane runs in customer-managed cluster infrastructure and handles all AI traffic. The two planes communicate over a single outbound HTTPS connection initiated by the data plane; no inbound connections from the internet reach the management plane.
Overview
- Prepare: obtain the credential and set install variables
- Mirror: copy TARE artifacts to the private registry
- Install: apply CRDs and deploy with Helm
- Verify and expose: confirm health, expose the service, and smoke test
- Operate
Prerequisites
- A TARE data plane credential from the dashboard, saved as
data-plane-credentials.json. tare,helm, andkubectlinstalled locally.- Access to the target Kubernetes cluster.
- Push access to the private registry mirror.
- Pull access from the target cluster to the same private registry.
Conventions
Later steps reuse the shell variables defined in Step 2 - Set variables, such as DP_CREDENTIAL and PRIVATE_IMAGE_REGISTRY. The steps assume a single shell session. If the session changes, re-export the variables before continuing.
Step 1 - Obtain the data plane credential
In the dashboard, go to Settings -> Data planes.
If this data plane is not registered yet:
- Click Register Data plane.
- Set Data plane ID to a stable ID such as
acme-prod. - Optionally set Name, Description, and Labels.
- Leave URL blank for now unless the final gateway hostname is already known.
- Click Register Data plane.
Then generate the credential from the data plane row:
- Click the row's edit action to open the Data plane drawer.
- In the Credential section, click Generate credential.
- Save the downloaded file as
data-plane-credentials.jsonon the machine the install runs from.
Each data plane uses its own credential. A credential can be rotated or revoked from the same Credential section in the data plane edit drawer.
Step 2 - Set variables
For Azure Container Registry:
DP_CREDENTIAL=./data-plane-credentials.json
PRIVATE_REGISTRY_HOST=${ACR_NAME}.azurecr.io
PRIVATE_IMAGE_REGISTRY=${ACR_NAME}.azurecr.io/tare
PULL_SECRET=tare-registry-pull
For another private registry:
DP_CREDENTIAL=./data-plane-credentials.json
PRIVATE_REGISTRY_HOST=registry.acme.example.com
PRIVATE_IMAGE_REGISTRY=registry.acme.example.com/tare
PULL_SECRET=tare-registry-pull
REGISTRY_USERNAME=<registry-user>
REGISTRY_PASSWORD=<registry-password-or-token>
Step 3 - Sync release artifacts
Log in locally with credentials that can push to the destination registry.
For ACR:
az acr login --name "${ACR_NAME}"
For a generic private registry:
printf '%s' "${REGISTRY_PASSWORD}" | \
docker login "${PRIVATE_REGISTRY_HOST}" \
--username "${REGISTRY_USERNAME}" \
--password-stdin
Copy the pinned TARE images and the serve-helm OCI chart:
tare install "${DP_CREDENTIAL}" \
--image-sync "${PRIVATE_IMAGE_REGISTRY}" \
--sync-only \
--parallel 2 \
--stall-threshold 5m
tare uses the data plane credential to pull from Tetrate's source registry. The local registry login is used only to push to the private registry.
Step 4 - Apply CRDs
Apply CRDs outside Helm:
tare install "${DP_CREDENTIAL}" \
--crds-only \
--image-registry "${PRIVATE_IMAGE_REGISTRY}"
CRDs are intentionally managed outside the Helm release so helm uninstall does not delete cluster-scoped APIs and cascade-delete custom resources.
Step 5 - Render Helm values
Render values for the Helm release:
tare install "${DP_CREDENTIAL}" \
--image-registry "${PRIVATE_IMAGE_REGISTRY}" \
--print-helm-values > values.yaml
Treat values.yaml as sensitive because it contains the data plane identity secret. Do not commit it as plaintext. Use SOPS, Sealed Secrets, External Secrets, or the organization's approved secret workflow.
If the public data plane URL is not known yet, make sure the rendered values do not set it to the management-plane API URL. Keep these fields empty:
global:
serveUrl: ""
controller:
config:
proxyUrl: ""
If needed, override them during Helm install:
--set-string global.serveUrl= \
--set-string controller.config.proxyUrl=
Step 6 - Create namespaces and pull secrets
Create the data plane namespaces:
kubectl create namespace tars-system --dry-run=client -o yaml | kubectl apply -f -
kubectl create namespace tars-dataplane --dry-run=client -o yaml | kubectl apply -f -
For AKS attached to ACR, no image pull secret is usually needed after:
az aks update \
--resource-group "${RESOURCE_GROUP}" \
--name "${AKS_CLUSTER_NAME}" \
--attach-acr "${ACR_NAME}"
For a private registry that requires Kubernetes pull credentials, create the same secret in both namespaces:
kubectl create secret docker-registry "${PULL_SECRET}" \
--docker-server="${PRIVATE_REGISTRY_HOST}" \
--docker-username="${REGISTRY_USERNAME}" \
--docker-password="${REGISTRY_PASSWORD}" \
--namespace tars-system \
--dry-run=client -o yaml | kubectl apply -f -
kubectl create secret docker-registry "${PULL_SECRET}" \
--docker-server="${PRIVATE_REGISTRY_HOST}" \
--docker-username="${REGISTRY_USERNAME}" \
--docker-password="${REGISTRY_PASSWORD}" \
--namespace tars-dataplane \
--dry-run=client -o yaml | kubectl apply -f -
When using a pull secret, render values with:
tare install "${DP_CREDENTIAL}" \
--image-registry "${PRIVATE_IMAGE_REGISTRY}" \
--image-pull-secret-name "${PULL_SECRET}" \
--print-helm-values > values.yaml
Step 7 - Install or upgrade with Helm
Use the chart version for the TARE release:
CHART_VERSION="0.1.0-alpha.1+17f076b"
Install or upgrade the release:
helm upgrade --install tars "oci://${PRIVATE_IMAGE_REGISTRY}/serve-helm" \
--version "${CHART_VERSION}" \
-f values.yaml \
-n tars-system \
--skip-crds \
--set ai-gateway-crds.enabled=false \
--set-string global.serveUrl= \
--set-string controller.config.proxyUrl=
Use --skip-crds and --set ai-gateway-crds.enabled=false together. The first skips chart crds/ entries; the second disables AI Gateway CRDs rendered as normal Helm templates.
Helm expects the SemVer build-metadata form with +, such as 0.1.0-alpha.1+17f076b. OCI stores the tag internally with _, but helm --version should still use +.
Step 8 - Verify
Check the Helm release:
helm list -n tars-system
helm status tars -n tars-system
Wait for workloads:
kubectl rollout status deployment/controller -n tars-system --timeout=300s
kubectl rollout status deployment/egress -n tars-dataplane --timeout=300s
Confirm images come from the private registry:
kubectl get deploy egress -n tars-dataplane \
-o jsonpath='{range .spec.template.spec.containers[*]}{.name}={.image}{"\n"}{end}'
Run doctor:
tare doctor "${DP_CREDENTIAL}" --verbose
A fresh data plane with no routes can report warnings for missing RouteDeployments, AIGatewayRoutes, HTTPRoutes, or EnvoyPatchPolicies. The core install is healthy when pods roll out and images pull from the private registry.
Step 9 - Expose the data plane
This Helm workflow installs the data plane but does not create a public Gateway, ingress, DNS record, or TLS certificate.
For gateway exposure, follow the published cloud install guide for the target platform, or use the organization's preferred ingress or Gateway stack.
The data plane serves traffic through the in-cluster egress service:
- Service: egress
- Namespace: tars-dataplane
- Port: 10080
- Paths:
/v1/*,/mcp/*, and/.well-known/*
After the gateway and DNS are ready, set the data plane URL in the dashboard: Settings -> Data planes -> edit row -> URL.
Step 10 - Validation and smoke tests
Validate the Helm install first:
helm status tars -n tars-system
kubectl rollout status deployment/controller -n tars-system --timeout=300s
kubectl rollout status deployment/egress -n tars-dataplane --timeout=300s
kubectl get deploy egress -n tars-dataplane \
-o jsonpath='{range .spec.template.spec.containers[*]}{.name}={.image}{"\n"}{end}'
tare doctor "${DP_CREDENTIAL}" --verbose
Then run traffic smoke tests after onboarding has configured provider keys and routes. This Helm workflow does not create a public Gateway or DNS record. Until the gateway is exposed, use a local port-forward to the in-cluster egress service:
kubectl port-forward -n tars-dataplane svc/egress 18080:10080
In another terminal, set the local endpoint and an API key from the router app:
export DP_SCHEME=http
export DP_HOST=127.0.0.1:18080
export TARS_API_KEY=<your-api-key-from-router-app>
At minimum, verify model listing with and without authentication:
curl -s -o /tmp/tare-models.json -w "%{http_code}\n" \
"${DP_SCHEME}://${DP_HOST}/v1/models" \
-H "Authorization: Bearer ${TARS_API_KEY}"
curl -s -o /tmp/tare-models-no-auth.json -w "%{http_code}\n" \
"${DP_SCHEME}://${DP_HOST}/v1/models"
Expected result: the authenticated request returns 200; the unauthenticated request returns 401.
Also smoke test every API shape enabled during onboarding, such as /v1/chat/completions, /v1/responses, /v1/messages, /v1/embeddings, /v1/images/generations, and MCP profile paths.
Upgrade and rollback
Use the same split workflow for upgrades: update CRDs first, then upgrade the Helm workload release.
If the new TARE release includes CRD changes, re-run CRD apply:
tare install "${DP_CREDENTIAL}" \
--crds-only \
--image-registry "${PRIVATE_IMAGE_REGISTRY}"
Render values for the new release:
tare install "${DP_CREDENTIAL}" \
--image-registry "${PRIVATE_IMAGE_REGISTRY}" \
--print-helm-values > values.yaml
If a pull secret is used, include it when rendering values:
tare install "${DP_CREDENTIAL}" \
--image-registry "${PRIVATE_IMAGE_REGISTRY}" \
--image-pull-secret-name "${PULL_SECRET}" \
--print-helm-values > values.yaml
Upgrade the workload release:
CHART_VERSION="<new-chart-version>"
helm upgrade tars "oci://${PRIVATE_IMAGE_REGISTRY}/serve-helm" \
--version "${CHART_VERSION}" \
-f values.yaml \
-n tars-system \
--skip-crds \
--set ai-gateway-crds.enabled=false \
--set-string global.serveUrl= \
--set-string controller.config.proxyUrl=
Rollback uses Helm's normal rollback flow:
helm rollback tars -n tars-system
CRDs are not affected by rollback because they are not part of the Helm release manifest. Existing custom resources remain in the cluster.
If artifacts are already mirrored
If the platform team has already mirrored the TARE images and chart, skip the --image-sync step and start at CRD apply:
tare install "${DP_CREDENTIAL}" \
--crds-only \
--image-registry "${PRIVATE_IMAGE_REGISTRY}"
Then render values and install with Helm as above.
Troubleshooting
Destination registry reports repository not found
Create the destination repository before syncing. For example, with Google Artifact Registry:
gcloud artifacts repositories create tare \
--repository-format docker \
--location <region>
ACR and many enterprise registries create repositories on first push, but some registries require pre-creation.
Helm reports invalid ownership metadata
This usually means Helm is trying to adopt CRDs already applied by tare --crds-only.
Example:
CustomResourceDefinition "aigatewayroutes.aigateway.envoyproxy.io" exists and cannot be imported into the current release: invalid ownership metadata
Install or upgrade with both:
--skip-crds \
--set ai-gateway-crds.enabled=false
Helm reports improper constraint
helm --version expects a SemVer constraint, not a bare commit SHA. Use the chart's full SemVer, such as 0.1.0-alpha.1+17f076b, or a release-build TARE version such as v0.1.0-beta.3.
Bare commit SHAs are rejected before Helm contacts the registry. The registry stores SemVer build metadata with _, but helm --version should use the SemVer form with +.
Pods are stuck in ImagePullBackOff
Check that:
- the private registry contains every image tag for the TARE release
- the cluster identity can pull from the private registry
- pull secrets exist in both tars-system and tars-dataplane when required
- the pull secret is referenced from
values.yaml; if not, re-render values with--image-pull-secret-name values.yamlpointsglobal.imageRegistryat the intended registry
Helm cannot pull the chart from the mirror
If helm install or helm upgrade fails locally with pull access denied, log in to the OCI registry from the operator machine:
helm registry login "${PRIVATE_REGISTRY_HOST}"
This is separate from Kubernetes image pull access. Helm pulls the chart artifact from the local machine; the cluster pulls workload images using node identity or image pull secrets.
Where to go next