Quick start (GKE)
The installer (install.sh) bootstraps everything you need for a running Platform Agent on GKE: cluster, CRDs, IAM, secrets, Chat integration, inference gateway, and, for a GitOps repository on GitHub, the token minter. It is a front door, not a second provisioner — the engine underneath is the Terraform composition terraform/examples/full-install, which owns every GCP resource and installs the kube-agents Helm chart, which owns every Kubernetes resource.
Prefer declarative IaC over the interview? Apply the same composition by hand with your own terraform.tfvars — see the composition README for the hand-driven path. Both paths run the same Terraform, so they create the same resources.
Run the Zero-Friction One-Liner Installer
Section titled “Run the Zero-Friction One-Liner Installer”Run the release-pinned installer (substituting <RELEASE_VERSION> with the desired release tag from GitHub Releases, for example 0.4.0; a GitLab install needs 0.9.0 or later):
curl -fsSL https://raw.githubusercontent.com/gke-labs/kube-agents/<RELEASE_VERSION>/install.sh | bashThe interactive installer is strongly recommended for initial installation: it detects context from your gcloud session and guides you through project selection, cluster options, and model provider credentials with minimal typing.
When running the release-pinned installer, the baked release version is used automatically.
For headless environments, AI Agents, or CI/CD automated deployments where an interactive TTY is unavailable, use --non-interactive with parameter flags:
curl -fsSL https://raw.githubusercontent.com/gke-labs/kube-agents/<RELEASE_VERSION>/install.sh | bash -s -- \ --non-interactive \ --gcp-project-id="my-gcp-project" \ --gke-cluster-name="platform-agent-host" \ --gcp-region="us-central1" \ --model-provider="gemini" \ --permission-set="read-only"Install with an AI coding assistant
Section titled “Install with an AI coding assistant”Give an AI coding assistant this prompt. It needs no checkout of the repository:
Install the latest official release of kube-agents (github.com/gke-labs/kube-agents) into my GCP project.Follow INSTALL.md from that release tag — do not invent installer URLs, namespaces, or model names.First inspect my gcloud project and existing GKE clusters and confirm the target, cluster, model provider,and credential with me. Run install.sh with --dry-run and show me its printed summary before you change anything.Only run the real install after I say yes.Interactive installer workflow
Section titled “Interactive installer workflow”-
Obtain the release sources.
Download and extract the self-contained release archive from GitHub Releases (recommended):
Terminal window curl -fsSL https://github.com/gke-labs/kube-agents/releases/download/<RELEASE_VERSION>/kube-agents-<RELEASE_VERSION>.tar.gz | tar -xzcd kube-agents-<RELEASE_VERSION>Alternatively, if you require a Git checkout, clone pinned to an official release tag (for example,
0.4.0; a GitLab install needs0.9.0or later):Terminal window git clone --branch <RELEASE_VERSION> https://github.com/gke-labs/kube-agents.gitcd kube-agents -
Run the installer. If you enable Slack, create the Slack app and its tokens first (Slack app setup): the installer asks for both tokens.
Terminal window ./install.shThe run is interactive: it prompts for the values it needs (project ID, region, new or existing cluster, the new cluster’s shape, chat integrations, model provider and API key, GitOps repo, agent permission set, gVisor, web UI, and memory). When run from an official release checkout or bundle,
install.shautomatically uses that release version and verifies that local files match the container images. Answers are saved toinstall.env(git-ignored,chmod 600), the install’s configuration;terraform/examples/full-install/terraform.tfvarsis regenerated from it on every run, so the two stay in step. A re-run loadsinstall.envfirst, so a flag you omit keeps the value it records instead of reverting to a default — with one exception: the installer never writes--github-pem-paththere. After the first import the private key lives in Cloud KMS, so a re-run needs neither the flag nor the file, and you are meant to delete the.pem. The installer then runs the composition’slifecycle.sh apply, and a re-run reconciles every change through oneterraform apply— safe to Ctrl-C and run again. -
Configure Google Chat (only if you enabled the Google Chat integration). In the Chat API console, point the bot’s Pub/Sub connection at the created topic (
projects/<PROJECT_ID>/topics/<CHAT_TOPIC_NAME>). The installer prints the exact topic name and step-by-step instructions when it finishes. -
Verify.
Terminal window kubectl get platformagents -n kubeagents-systemkubectl get pods -n kubeagents-systemYou should see one
PlatformAgentresource plus pods for the operator (kube-agents-controller-manager), LiteLLM (litellm), and three for the Platform Agent itself: the gateway (platform-agent-gateway), the shell sandbox (platform-agent-shell), and the credential broker (platform-agent-credential-proxy). The GitHub token minter (github-token-minter) also appears if you enabled the GitHub integration. When all three agent pods areRunningand Google Chat is configured, DM your Chat app and it should reply.The gateway logs errors while it starts up, and they do not mean the install failed. Repeated
Connection refusedfrom the Google Chat relay, and a one-offslack connect timed out after 30s, are ordering: both relays dial the credential broker before it is listening. They retry until it is, and on a fresh install the messages stopped on their own within about seven minutes. Treat them as a fault only if they are still arriving well after that, and inspect theenvoy-credential-proxycontainer in theplatform-agent-credential-proxypod rather than the relay reporting the error.If you enabled Slack, check that the credential broker authenticated the bot token (Slack app setup has the full check, including a test DM):
Terminal window kubectl logs deployment/platform-agent-credential-proxy -n kubeagents-system -c envoy-credential-proxy | grep -E 'Slack relay (enabled|initialization)|Slack bot token'Slack relay enabled workspaces=1(one per bot token) means the bot is connected.Slack bot token authentication failednames the Slack error for a token Slack refused, andSlack relay initialization failed; retryingrepeats every 30 seconds until one token authenticates. The pattern leaves outSlack relay operation failed, which is one Slack call the agent made failing later, not the connection.A first install runs
spec.mode: nextunless you passed--mode=todayor mirrored its third-party images, and anextinstall runs more pods and reports more conditions, and with Slack as its only chat platform its Slack connects from a different pod: check it with Verify aspec.mode: nextinstall as well. -
Talk to the agent.
With Google Chat or Slack enabled, DM the app. Both are opt-in and off by default, so an install that enabled neither talks to the agent over
kubectl execinstead — the installer prints these two commands when you choose “None” at its chat prompt and again when it finishes, with your cluster, region, project and namespace already filled in:Terminal window gcloud container clusters get-credentials <CLUSTER_NAME> --location <REGION> --project <PROJECT_ID> --dns-endpointkubectl exec -it deployment/platform-agent-gateway -n kubeagents-system -c platform-agent -- hermes -p platformDrop
--dns-endpointon a cluster that publishes no externally reachable DNS endpoint, wheregcloudrejects it.-p platformreaches the Platform Agent directly; a barehermesreaches the Planning Agent front door, which is where a chat message would have landed. ChatOps covers the difference and what a chat-less install does not exercise.
Verify a spec.mode: next install
Section titled “Verify a spec.mode: next install”A first install runs spec.mode: next unless you pass --mode=today, or mirror the third-party images with --third-party-registry-prefix, which installs today unless you pass --mode=next. next puts the A2A stack in front of the agent: the NATS bus, its auth callout, the capability verifier, the A2A console and the A2A gateway, which takes the chat backend. Common flags and toggles covers the flag and how a later run keeps the mode. These checks come after step 4’s, against the same namespace and the PlatformAgent named platform-agent.
-
Confirm the mode.
Terminal window kubectl get platformagent platform-agent -n kubeagents-system -o jsonpath='{.spec.mode}{"\n"}'It prints
next. An empty line meanstoday, and nothing below applies. -
Wait for
Ready. Undernext,Readyalso waits for the NATS StatefulSet, the auth callout, the first completion of the bus provisioning Job and, once a chat backend is configured, the A2A gateway. A first boot can take over fifteen minutes.Terminal window kubectl wait platformagent/platform-agent -n kubeagents-system --for=condition=Ready --timeout=25mWhile it waits, the phase reads
Provisioning. Once the agent’s own pod is ready, theReadycondition’s message names the A2A workload it is waiting on; before that it readsWaiting for deployment replicas to be ready. That is a slow start.phase: Degradedwith reasonA2AProvisionFailedis a broken one: the provisioning Job failed, and its pod log says why. -
Read the A2A conditions.
Terminal window kubectl get platformagent platform-agent -n kubeagents-system \-o jsonpath='{range .status.conditions[*]}{.type}{"\t"}{.status}{"\t"}{.reason}{"\n"}{end}'A healthy
nextinstall shows the three rows below, and theReadymessage readsGateway, shell sandbox, credential broker, NATS, auth callout, bus provisioning and A2A gateway are all ready:Type Status Reason ReadyTrueReconciledBusProvisionedTrueProvisionJobCompleteBusCredentialsReadyTrueCalloutServingA2AGatewayandA2AVerifierappear only when something needs a look.A2AGatewayFalsewithNoChatBackendmeans no chat backend is enabled, so the operator runs no A2A gateway, and the install still reachesReadywith a message saying so; withWaitingForReplicaa backend has come back and the gateway’s replica is not ready yet.A2AVerifierFalsewithVerifierNotReadymeans no verifier replica is ready, and every task submitted meanwhile is refused.BusCredentialsReadyFalsewithCalloutAbsentmeans no auth callout is deployed and nothing can authenticate to the bus. WithCalloutUnavailable, fewer than all callout replicas are ready on the current spec, which every rollout of the callout passes through; the message gives the counts, and only with none ready does the bus refuse every new connection. The status reference has every condition. -
List the A2A pods. Every pod the A2A stack runs carries the label
app.kubernetes.io/part-of=a2a-next, so this lists them apart from step 4’s pods:Terminal window kubectl get pods -n kubeagents-system -l app.kubernetes.io/part-of=a2a-nextPod How many Expected state platform-agent-a2a-nats-01 Runningplatform-agent-a2a-callout-…2 Runningplatform-agent-a2a-verifier-…2 Runningplatform-agent-a2a-console-…1 Runningplatform-agent-a2a-gateway-…1, with a chat backend only Runningplatform-agent-a2a-provision-<hash>-…1 per run, more if it retries Completed; a failed attempt staysError; all removed 24 hours after the Job finishes<profile>-<animal>-<hex>1 per session For a /sessionconversation,Runninguntil 30 minutes after its last message, and for 4 hours at most; for adelegatemessage,Runningwhile its one task runsThe A2A gateway creates a session pod only for a conversation switched to
/sessionor a message that starts withdelegate; other tasks run in the agent pod. It runs up tospec.harness.tuning.maxSessions(10 by default) at once, and a fresh install has none. -
Check the agent’s bridge. The operator adds a
hermes-bridgecontainer to the agent pod once the bus is provisioned, so theplatform-agent-gatewaypod is replaced once during a first boot. The bridge takes tasks off the bus and runs them on the agent’s API server.Terminal window kubectl get deployment platform-agent-gateway -n kubeagents-system -o jsonpath='{.spec.template.spec.containers[*].name}{"\n"}'kubectl logs deployment/platform-agent-gateway -n kubeagents-system -c hermes-bridge | grep 'hermes bridge consuming'The first command lists
hermes-bridgeamong the containers. The second prints one JSON line with"msg":"hermes bridge consuming","profile":"platform"and"executor":"api"."executor":"cli"means the operator setsA2A_BRIDGE_EXECUTOR=cli, and the bridge runs each task as its ownhermes chatprocess instead. APlatformAgentthat declares its ownhermes-bridgesidecar inspec.deployment.sidecarskeeps it, the operator adds none, and that sidecar’s executor is whatever its own environment selects. -
Check Slack, if you enabled it. With Slack as the only chat platform enabled, the A2A gateway holds Slack’s Socket Mode connection and the credential broker’s Slack relay from step 4 does not run. First confirm which backend the gateway took:
Terminal window kubectl logs deployment/platform-agent-a2a-gateway -n kubeagents-system -c gateway | grep '"msg":"a2a gateway starting"'"backend":"slack"means the gateway holds Slack; check the connection with thenextsteps in Slack app setup."backend":"gchat"means Google Chat is enabled too and holds the gateway: Slack is then answered by the Hermes Slack platform, as undertoday, and step 4’s check applies instead. A Slack failure undernextshows only in the gateway’s log, never in thePlatformAgent’s.status: a refused token logschat backend stopped; restarting it, the console stays upand the gateway retries with a backoff, while the pod staysRunning.
Startup reads differently under next. On a first boot the operator creates the A2A gateway only once an auth callout replica is serving, and adds the bridge only once the provisioning Job has completed, so for the first several minutes there is no gateway pod and the agent pod has no hermes-bridge container; the Ready message names what is still missing. The A2A gateway reads the Slack tokens from the install’s Secret and dials Slack itself, so the one-off slack connect timed out after 30s from step 4 is not expected under next. If it does appear, the Hermes Slack platform is running, which under next means Google Chat is enabled as well.
What just happened
Section titled “What just happened”The installer collected your answers, wrote install.env and terraform.tfvars, and ran one terraform apply through the composition’s lifecycle.sh — GCP APIs, the GKE cluster (CMEK, Workload Identity, the host-discovery label), IAM and Workload Identity bindings, cert-manager, the drift detector’s Log Router sink, Pub/Sub topic and pull subscription, and the Helm chart carrying the operator, the PlatformAgent CR, the credentials Secret, and the LiteLLM gateway. Terraform state lives in a versioned GCS bucket (<project>-kube-agents-tfstate, prefix kube-agents/<cluster>), so uninstall.sh and upgrade.sh can find the install from a fresh clone.
When adopting an existing cluster, four settings stay gcloud pre-steps because a Terraform data source cannot express them: CMEK database encryption, Workload Identity pool enablement, node-pool migration to GKE_METADATA (requires --migrate-node-pools), and legacy Calico NetworkPolicy enforcement (requires --enable-network-policy; --accept-no-network-policy installs without it instead and changes nothing on the cluster). On freshly created clusters, setting the managed OpenTelemetry collection scope stays a gcloud post-step (no provider field exists). The one-shot GitHub App key import into KMS also runs outside Terraform, so the PEM never enters state.
Changing your mind later is the same engine: running the installer with --menu (e.g. ./install.sh --menu or $HOME/kube-agents/install.sh --menu) opens a Day-2 control panel whose Save & Apply regenerates terraform.tfvars and re-applies.
Common flags and toggles
Section titled “Common flags and toggles”--generate-only— writesinstall.env(when absent) andterraform.tfvars, runs pre-apply validation checks, prints out-of-Terraform prerequisites andlifecycle.shcommands, and exits 0 without applying. Those checks include the existing-cluster consent gates, so adopting a cluster that needs--migrate-node-pools, or one that enforces no NetworkPolicy and was given neither--enable-network-policynor--accept-no-network-policy, exits 1 instead; INSTALL.md is canonical for the mode.--dry-run— always runsterraform validate, and adds a fullterraform planpreview when Application Default Credentials exist (on an adopted cluster lacking Workload Identity, or lacking NetworkPolicy enforcement without--accept-no-network-policy, the plan is skipped with remediation commands); nothing is created, and state stays local. It writes noinstall.env: a dry run provisions nothing, so it has no install to record.--gke-cluster-mode=autopilot|standard— the shape of a cluster this run creates;autopilotunless you say otherwise, and the interactive installer asks when it is about to create one. Autopilot clusters are regional, and what a zonal--gcp-regiondoes depends on who chose Autopilot: passing--gke-cluster-mode=autopilotexplicitly aborts at the interview, while leaving the flag unset builds Standard instead, since nothing asked for Autopilot there. An invalid mode value aborts the run whatever the target is. The chosen shape has no bearing on a cluster that already exists: the installer writes that cluster’s probed shape instead, for the reasonscripts/installer/README.mdgives.--migrate-node-pools— authorizes migrating existing node pools using the legacy GCE metadata server toGKE_METADATAwhen adopting a cluster (recreates nodes and restarts workloads; required on clusters with legacy pools, else the install aborts).--enable-network-policy— authorizes enabling the legacy Calico NetworkPolicy addon and node enforcement when adopting a GKE Standard cluster without Dataplane V2 (may recreate nodes and restart workloads). One of two answers for such a cluster; with neither, the install aborts unchanged.--accept-no-network-policy— the other answer: install onto that cluster without modifying it. Every NetworkPolicy the install ships is then inert, the agent sandbox’s included, and the choice is recorded in the install report and on thePlatformAgent. Installing without NetworkPolicy enforcement says exactly what that costs.--enable-gvisor=false— runs the agent on the standard container runtime. The default istrue: the agent executes model-authored commands, so it runs under thegvisorRuntimeClass unless you say otherwise. Autopilot, the default shape, ships that RuntimeClass and needs no node pool, from GKE 1.27.4-gke.800 onwards — the installer stops before applying anything if an Autopilot cluster it is adopting is older than that. On a Standard cluster the sandbox needs a node pool of its own, which the run provisions (thegke-clustermodule’senable_gvisor_node_pool).--enable-google-chat— provisions the Google Chat Pub/Sub backend and enables the CR’sgoogleChatintegration.--enable-slack— enables the CR’sslackintegration. It needs--slack-bot-tokenand--slack-app-token; a non-interactive run missing either aborts before applying, since the relay cannot open a socket without both.--slack-allowed-users(Slack member IDs such asU0123ABCD, not emails),--slack-home-channel, and--slack-home-channel-nameare optional, and an empty allowlist allows everyone in the workspace. Create the app and its tokens first: Slack app setup.- On a later run, the chat flags must agree with
install.env.upgrade.shrenders Slack and Google Chat from that file and takes no chat flag, soinstall.shrefuses--enable-slack,--enable-google-chator a--slack-*,--google-chat-allowed-users,--google-chat-home-channel,--google-chat-modeor--chat-topic-namevalue that disagrees with a key the file sets; change the key instead. A key the file does not set is recorded there with the value the run applies, except the Slack tokens, which later runs recover from the cluster.scripts/installer/README.mdhas the full rule. --agent-namespace=NAME— the namespace the agent and operator are installed into;kubeagents-systemunless you say otherwise.--mode=next|today— thePlatformAgent’sspec.mode;nexton a first install unless you say otherwise, which puts the NATS bus and the A2A gateway in front of the agent.todayis the previous stack, kept as a rollback for one release. A first install that mirrors its third-party images with--third-party-registry-prefixgetstodayunless you pass--mode=next, because nothing in the install pointsnext’s NATS images at the mirror. When neither--modenorinstall.envnames a mode, a cluster that already runs kube-agents keeps the mode it runs,todayincluded. The first install records the mode ininstall.envasPLATFORM_AGENT_MODE; with aninstall.envyou wrote yourself,--modeappends the key when the file sets none. To switch a running install, edit that key and re-runinstall.shor a fullupgrade.sh, which say it is a mode switch before they apply it; a--modethat disagrees with the file is refused, because the next run would switch back.scripts/installer/README.mdis canonical for the key.- Under
next(a first install’s default), Slack is answered by the A2A gateway only when it is the one chat platform enabled. With Google Chat enabled too, Google Chat holds the gateway and Slack stays on the Hermes Slack platform. The interactive installer’s chat menu pre-selects Google Chat on a first install, so choose Slack (Socket Mode App) there, or pass--enable-slackwithout--enable-google-chat; on an install whoseinstall.envalready recordsGOOGLE_CHAT_ENABLED=true, set it tofalsethere instead. The A2A gateway also serves one Slack workspace: pass one bot token in--slack-bot-token. With a comma-separated list the gateway fails to authenticate, Slack goes unanswered, and only the gateway pod’s log says why. --enable-gke-backup-plan— provisions the GKE Backup plan for the cluster. Left unset, the composition’s own default decides.--enable-drift-detector=false— installs without out-of-band change detection. The default istrue: a cluster reconciled from a GitOps repository is one where a change made outside that repository is worth reporting, and nothing else reports it. Turning it on costs three GCP resources in the install’s project — a Log Router sink, a Pub/Sub topic and a pull subscription — and a sink in each project a declared scope lists, and it reaches past the clusters this install manages: the sinks export the admin-activity audit records of every GKE cluster in those projects, including clusters this install does not manage, and the subscription retains a copy of them for 31 days. On this first install the flag sticks:install.shwritesinstall.envand records the value you passed. It never rewrites that key afterwards, so on a later run the same flag applies to that run alone — which is why the durable opt-out is anENABLE_DRIFT_DETECTOR=falseline ininstall.env.spec.harness.driftDetectorhas the rest: what the detector reports, what it drops, and why the file is the place to opt out.--gitops-org=ORG,--gitops-repo=REPO,--github-app-id=ID,--github-pem-path=PATH— configures GitOps PR automation via the in-cluster GitHub token minter (Minty). When--github-pem-pathis provided,install.shimports the private key into Cloud KMS during install; alternatively, pre-provision the key AOT beforehand (see Token minter).--gitops-forge=gitlab,--gitops-host=HOST,--gitlab-token-file=PATH,--gitops-ca-file=PATH— connects a GitOps project on gitlab.com or a self-managed GitLab instead, with no token minter.--gitops-ca-filegives the private CA that signed a self-managed instance’s certificate. See GitLab as the GitOps forge.--gitops-host=HOST,--gitops-ca-file=PATHwith the GitHub flags above — connects a GitOps repository on a GitHub Enterprise Server, with the GitHub App created on the instance. See GitHub Enterprise Server in INSTALL.md.--memory=file|hindsight|off— the agent’s long-term memory store;hindsightdeploys the Hindsight API and Postgres into the cluster.
Run the installer with --help (e.g. ./install.sh --help) for the full list.
Operator settings
Section titled “Operator settings”Some operator behaviour is set by environment variables on the operator’s controller manager, such as A2A_NATS_IMAGE and A2A_PROVISION_IMAGE, the images a mirrored spec.mode: next install points at its own registry. install.sh has no flag for them. Set them in the chart’s operator.extraEnv list, which is appended after the variables the chart renders and so wins over them. On an install made with install.sh, put the list in a TF_VAR_extra_helm_values line in install.env, which every later install.sh and upgrade.sh run reads:
TF_VAR_extra_helm_values='{operator={extraEnv=[{name="A2A_NATS_IMAGE",value="<REGISTRY>/nats:<TAG>"},{name="A2A_PROVISION_IMAGE",value="<REGISTRY>/nats-box:<TAG>"}]}}'The value is an HCL object that Helm merges over the chart values the composition computes, so the same line can carry other chart keys; there is one such line, so put every extra value in it. Do not edit terraform/examples/full-install/terraform.tfvars for this: the installer regenerates that file on every run. Check it took after the next run:
kubectl get deployment kube-agents-controller-manager -n kubeagents-system -o jsonpath='{range .spec.template.spec.containers[0].env[*]}{.name}={.value}{"\n"}{end}' | grep '^A2A_'On a hand-driven Terraform apply, set extra_helm_values in terraform.tfvars instead; on a bare Helm install, set operator.extraEnv in your values file.
Upgrade
Section titled “Upgrade”Run the upgrade.sh published for the release you are moving to; it carries that version, so the run names no tag. See Upgrade for the modes, the previews, and what a run refuses.
Uninstall
Section titled “Uninstall”Run ./uninstall.sh — it drives the same composition’s lifecycle.sh destroy against the state in GCS. See Uninstall for the full procedure and the asymmetries it handles.
Next steps
Section titled “Next steps”- Concepts → Platform Agent — what the agent’s persona is doing and why.
- Concepts → ChatOps — how humans talk to it, and how proactive alerts come back.
- Skill catalog — everything the agent can do out of the box.