Kagent is an open-source Kubernetes operator for AI agents. It allows you to write a custom resource describing what the agent should be, apply it, and a controller turns it into a running workload. Thirty lines of YAML, and you have an agent in a pod.
I ran a small agent fleet on EKS with[kagent]to experience for myself its capabilities, learn more AI platform engineering and to properly understand the boundaries of what it provides.
The next question is then, what was just put in your cluster, and what else is there to do? An agent is a long-lived workload that talks to a model, calls tools that touch real systems, holds state, and possibly+probably talks to other agents. Some of these surfaces the operator now owns for you while it deliberately doesn’t own others.
Here’s a map for illustration:
In short: kagent owns the workload, the reasoning loop, tool registration, session state. You still own namespaces, network policy, secrets, identity, etc. The same platform work as everything else you run.
The operator does not own a clean bottom half of the stack. Each layer is divided, and the right-hand side is ordinary platform work — quota, egress, secrets, RBAC, backup, identity.
The core of kagent is the ability to describe an agent as a Kubernetes object. A Go controller reconciles it into the actual workload.
Its manifest would look something like this:
apiVersion: kagent.dev/v1alpha2kind: Agentmetadata:name: cluster-diagnosticsnamespace: kagentspec:type: Declarativedeclarative:modelConfig: default-model-configsystemMessage: |You are a read-only Kubernetes troubleshooting specialist…tools:- type: McpServermcpServer:kind: RemoteMCPServername: kagent-tool-servertoolNames: [k8s_get_resources, k8s_get_events, k8s_get_pod_logs]
Apply that and the controller produces a Deployment, a Service, a ServiceAccount with the RBAC that workload needs, and a Secret holding the rendered agent config. With that, Agents have become declarative Kubernetes objects that can be versioned and rolled out with GitOps.
Here’s what’s in my kagent namespace. Here a single namespace is used for ease of exploration and illustration. This isn’t a stock install, took the command output after switching off several built-in agents and adding a few custom agents and MCP servers as i was exploring.
As can be seen, there’s an operator, a database, a tool server, a web UI, and a set of prebuilt agents that work the moment the kagent chart lands — k8s-agent, helm-agent, istio-agent, promql-agent and others.
$ kubectl -n kagent get podskagent-controller-cf74f9f96-kwblb 1/1 Running # the operatorkagent-postgresql-6c47c5bc6f-hg6cz 1/1 Running # state storekagent-tools-56494b5564-7zsdn 1/1 Running # built-in MCP tool serverkagent-kmcp-controller-manager-76bb479b6-4s8z4 1/1 Running # kmcp — build/deploy your own MCP serverskagent-ui-69cf9cd7cf-rpbmr 1/1 Running # web UIkagent-default-67c785f9db-wnzc2 1/1 Running # part of the kagent installhelm-agent-5fd78944d8-wzdkx 1/1 Running # built-in agentistio-agent-6dfb7b5f-xgm6v 1/1 Running # built-in agentk8s-agent-9f9548bdc-mnrdb 1/1 Running # built-in agentpromql-agent-f7cb48786-tf5jb 1/1 Running # built-in agentcloud-diagnostics-5cf9fc9684-ppg6v 1/1 Running # minecluster-diagnostics-756cf6455b-vdpgz 1/1 Running # minecluster-remediation-76ccf4f698-xf99n 1/1 Running # mineincident-commander-5c89f99d6d-rrpdp 1/1 Running # mine (orchestrator)investigation-loop-797bbc9f7c-kp87w 1/1 Running # mine (BYO)cost-sentinel-644f7c6c44-kwxss 1/1 Running # mineaws-documentation-76b96d6c8f-tgjzj 1/1 Running # mine (MCP server)aws-eks-57977cb77d-mhgbq 1/1 Running # mine (MCP server)aws-pricing-9576cf679-m9722 1/1 Running # mine (MCP server)agent-sandbox-probe 1/1 Running # mine (sandbox experiment)
Looking at the API surface itself (by k8s API group), kagent comes with 9 custom resources under 1 API group. Each is a thing that used to live inside an agent application when needed; and now they live in the cluster API.
$ kubectl get crd -o custom-columns=NAME:.metadata.name,GROUP:.spec.group --no-headers \ | grep 'kagent.dev$' | sortagentharnesses.kagent.dev kagent.devagents.kagent.dev kagent.devmcpservers.kagent.dev kagent.devmemories.kagent.dev kagent.devmodelconfigs.kagent.dev kagent.devmodelproviderconfigs.kagent.dev kagent.devremotemcpservers.kagent.dev kagent.devsandboxagents.kagent.dev kagent.devtoolservers.kagent.dev kagent.dev`ModelConfig` and `ModelProviderConfig` hold the provider, the model, and the credential reference. Agents can then point to them by name (refer to yaml above). Change a model, or move a model provider’s key, and you’re editing a namespaced object without needing to redeploy agents. (But of course, the fact that prompts and tool-calling behaviour are model-specific needs to be accounted for when changing this shared object.)
While some of what kagent owns is a CRD you create, some are just a field on the Agent you already have. For examplerequireApproval allows for human-in-the-loop and is nested inside the tool reference:
``` bash
$ kubectl explain agent.spec.declarative.tools.mcpServerFIELDS: allowedHeaders <[]string> apiGroup <string> kind <string> name <string> -required- namespace <string> requireApproval <[]string>
kagent runs two kinds of agent. The CR spec.type is an enum with exactly two values, Declarative and BYO.
Declarative agents use kagent’s engine, you supply a system prompt, a tool list, a model config and the ADK (Google’s Agent Development Kit framework) owns the model calls, tool dispatch, retries and context handling. A capable multi-tool agent can be just ~30 lines of YAML or less. Kagent ships two runtime implementations of pythonand go, selectable per agent. Both run the agent as an HTTP service and speak the same protocols.
BYO (bring-your-own) agents replace the engine. You ship a container that implements the loop yourself; kagent deploys it and routes messages to it. One of mine, investigation-loop, is a BYO LangGraph StateGraph. Just to illustrate a comparison point on looping, here’s pseudo code:
A Declarative agent loops too without a custom build; in fact every tool-calling agent does. What the BYO agent changes is who decides the branch. In a Declarative agent, “should I investigate further or answer now?” is a judgement the model makes inside its loop, shaped by the prompt. In the code above, it’s should_continue, a Python function that can be unit-tested, with a termination condition that can be asserted on and an iteration count that can be bound in code.
Something still has to act on the decision each time regardless whether its dispatching the tool, feeding the result back or stopping a loop that won’t converge. That’s the ADK, and we can see the difference between the declarative agent (manifest shown in section 2) and BYO agent by checking their image:
#Declarative - don't have to supply container image$ kubectl -n kagent get deploy cluster-diagnostics -o jsonpath='{..image}'cr.kagent.dev/kagent-dev/kagent/app@sha256:d4be3183...#BYO - custom image$ kubectl -n kagent get deploy investigation-loop -o jsonpath='{..image}'<MY_ACCOUNT_ID_REDACTED>.dkr.ecr.ap-southeast-1.amazonaws.com/aria/investigation-loop:latest
A Declarative agent’s reasoning runs inside an image kagent publishes and reinherits on every upgrade. A BYO agent’s runs inside yours.
What other differences are there?
$ kubectl explain agent.spec.byoFIELD: byo <Object>DESCRIPTION: BYO configures a "bring your own" agent backed by a user-provided container image. Kagent deploys the image and expects it to serve the agent over the A2A protocol on port 8080. Required if type is BYO.FIELDS: deployment <Object>
The BYO CR only offers a single field = deployment. Meanwhile modelConfig, tools, memory, context, and the approval gate all live under spec.declarativeand aren’t available for BYO.
So, much of the abstraction benefits mentioned earlier only apply to declarative agents. I would say that a good decision guideline would be to go declarative unless you can name what’s unusual about your loop.
Additionally, I would say, (as someone exploring platform work and not a dedicated AI app/agent developer for now), declarative agents can be really useful for iterating and building infra ops related agents. Here’s the current inventory of my platform in progress:
$ kubectl -n kagent get agents -o custom-columns=NAME:.metadata.name,TYPE:.spec.typeNAME TYPEcloud-diagnostics Declarativecluster-diagnostics Declarativecluster-remediation Declarativecost-sentinel Declarativedeploy-diagnostics Declarativehelm-agent Declarativeincident-commander Declarativeinvestigation-loop BYOistio-agent Declarativek8s-agent Declarativepromql-agent Declarative
The model<->agent cycle is not kagent-specific since every agent framework has it. The tool call is a network hop to a separate workload (an MCP server, another agent, etc.) with its own credential, rather than an in-process function; and the controller is an optional front door, not a hop. Every agent has its own Service, agent-to-agent calls go pod to pod, and keeping request traffic off the controller keeps the dataplane decoupled from reconciliation. The number of round trips is decided by the model at request time, which is where cost and latency actually come from.
This diagram was easily mapped since calling my incident commander agent returned a task object whose history[] records every step it took. What happened was the the model asked for a tool instead of answering. It emitted a function_call — name: kagent__NS__cluster_diagnostics, with an args.request spelling out what it wanted to know. The call travelled as A2A, pod to pod, and landed on the cluster diagnostics agent. Sub-agents are declared in the very same tools array as MCP servers, as peers (refer to the declarative agent manifest from section 2 for how the configuration looks like). Let’s double check:
$ kubectl -n kagent get agent incident-commander -o jsonpath='{.spec.declarative.tools}'[{"type":"Agent","agent":{"kind":"Agent","name":"cluster-diagnostics","namespace":"kagent"}}, {"type":"Agent","agent":{"kind":"Agent","name":"cloud-diagnostics", "namespace":"kagent"}}, {"type":"Agent","agent":{"kind":"Agent","name":"cost-sentinel", "namespace":"kagent"}}, {"type":"Agent","agent":{"kind":"Agent","name":"investigation-loop", "namespace":"kagent"}}]
You can see from above that sub-agents are a tool type at kubernetes CRD-level.
The subagent’s answer came back as a function_response — its own token usage attached: 3,588 prompt tokens, just for that one sub-question. With that result now in context, the orchestrator's model was called once more, and this time it wrote text instead of asking for another tool. That's what ends the loop — not a signal, just the absence of another function_call. Task state: completed.
Two protocols carry everything else an agent does outside its own process.
kagent reaches toward one more layer the original operator explicitly left alone, which is where the agent actually runs at the kernel level. There’s a resource for it calledSandboxAgent and it's a full peer of the regular Agent CR. The sandboxAgent carries its own type, declarative, byo. Which isolation backend runs it is decided by which config block you fill in, not an enum:
$ kubectl get crd sandboxagents.kagent.dev \ -o jsonpath='{...spec.properties.sandbox.description}'Sandbox configures sandboxed execution behavior shared across runtimes.This is intended for sandboxed declarative execution today, and can alsobe consumed by BYO agents.$ kubectl get crd sandboxagents.kagent.dev \ -o jsonpath='{...spec.properties.substrate.description}'Substrate is optional Agent Substrate-specific settings.
Looking at two separate projects that SandboxAgent can point at,
Agent Substrate is kagent’s own family, same Linux Foundation project umbrella, a first-class page in kagent’s core-concepts docs, and the exclusive runtime for AgentHarness. Agent Sandbox is a Kubernetes SIG Apps subproject , its own controller, its own CRDs — installed and operated entirely separately, and absent from kagent's own documentation despite being a real, working option.
Whichever you pick, the cost is the same shape: a second control plane to run and upgrade on top of the one you already have.
Kagent on Kubernetes: What Does it Give Your AI Platform? was originally published in Towards AI on Medium, where people are continuing the conversation by highlighting and responding to this story.