For the complete documentation index, see llms.txt. Markdown versions of all docs pages are available by appending .md to any docs URL.
Bring your own agent
Run a container image of your own as a kagent agent by implementing the A2A service that the byo runtime expects.
The byo runtime runs a container image that you build, and treats what is inside it as opaque. kagent still compiles the HarnessHarnessA Kubernetes custom resource defining how an agent is allowed to run: its runtime, workload image, WorkerPool and snapshot storage, and which AgentTemplates it accepts.Learn more and AgentTemplateAgentTemplateA Kubernetes custom resource defining what an agent does: its model, system prompt, tools, skills, and plugins. It runs only once a Harness accepts it.Learn more into a revisionRevisionThe compiled, immutable output of one Harness and AgentTemplate pairing, identified by a content digest. An AgentInstance runs the revision it was created from for its whole life, so editing either resource affects only instances created afterward., schedules that revision onto Agent SubstrateAgent SubstrateThe runtime that kagent runs agents on. It multiplexes many sandboxed Actors onto a smaller pool of pre-started Workers, suspending idle ones to snapshots.Learn more, and routes conversations to it. What the image does with a message is yours to decide. Choose this runtime when you have an agent framework that kagent does not adapt, and you would rather bring the image than the integration.
What kagent runs and what your image runs
The byo runtime divides the work at the A2AA2AThe Agent-to-Agent protocol, which callers and other agents use to talk to an AgentInstance. The conversation's context identifier is the AgentInstance ID, so a second message on the same ID continues the same conversation.Learn more (Agent-to-Agent) boundary. Everything on kagent’s side of that boundary behaves in the same way as for the built-in runtimes, so a BYO agent is sandboxed, snapshotted, and addressed identically to one that kagent executes itself.
kagent owns the lifecycle, the isolation, and the routing:
- Compiles a Harness and an AgentTemplate into an immutable revision, and creates an AgentInstanceAgentInstanceA running, conversational pairing of a Harness and an AgentTemplate. Unlike the two, it is not a Kubernetes resource: kagent's gRPC API creates it and its database tracks it.Learn more from it.
- Wraps the ActorActorThe sandboxed unit of compute, provided by Agent Substrate, that runs an AgentInstance's conversation loop. Every AgentInstance is backed by one.Learn more that the image runs in with a gVisor sandbox.
- Suspends and resumes the Actor between turns, including its in-memory state.
- Routes every conversation through the A2A gateway, so callers address the AgentInstance rather than the Actor behind it.
- Delivers the compiled agent configuration and agent card to the container as environment variables.
Your image owns the agent’s behavior and the two endpoints that expose it:
- An A2A service that accepts a message and returns a reply.
- A readiness endpoint that reports when the service can take traffic.
- Whatever the agent actually does: model calls, tool calls, and conversation state.
Because Agent Substrate snapshots the whole Actor, an image that keeps conversation state in memory keeps it across a suspend. Your image does not need to persist anything to survive the gap between turns.
The A2A contract
A byo image must meet four requirements to run as an agent. kagent enforces only spec.workload.command at apply time, and the rest surface as a failed invoke rather than as a validation error. The steps in the Configure a byo Harness and Build the image sections later in this page show how to satisfy each requirement.
| Requirement | Detail |
|---|---|
spec.workload.command is set on the Harness | A byo Harness must override the image entrypoint, because kagent does not infer it. |
| The container serves gRPC on port 80 | kagent builds the agent card with a single interface, http://127.0.0.1:80 bound to gRPC, and that address is not configurable. The service is lf.a2a.v1.A2AService. |
The container answers GET /readyz on port 8081 | Agent Substrate probes this path to decide when the Actor is ready, with a 30-second timeout. The probe is on a different port from the A2A service on purpose, so serve it independently. |
| The A2A service speaks gRPC, not JSON-RPC | The agent card fixes the protocol binding to gRPC. An image that serves A2A over JSON-RPC alone is never reached, whatever port it listens on. |
Warning
A byo Harness injects no PORT variable, and an image that listens elsewhere still reports READY. Readiness is probed on port 8081, which succeeds no matter what the A2A service does, so nothing surfaces the mismatch until an invoke fails with Connect: tunnel failed. Either pin port 80 in the image, or set PORT in the Harness’s spec.env as the examples on this page do. This gap is tracked as kagent#2758.
Opaque and configured agents
kagent compiles an AgentTemplate for a byo Harness in the same way as for the kagent runtime, then hands the result to the container as environment variables. The two ways of using this runtime differ only in whether the image reads them.
- An opaque agent ignores the compiled configuration. The AgentTemplate exists to give the agent an identity and a description, and the image decides everything else, including which model to call and which tools to offer. Every field on an opaque AgentTemplate is optional,
modelConfigincluded. - A configured agent reads the compiled configuration. The image honors the AgentTemplate’s system prompt, ModelConfig, tool bindings, skills, and plugins, so an operator changes the agent’s behavior by editing the AgentTemplate rather than by rebuilding the image.
The kagent and byo runtimes compile through the same path, so a configured BYO agent accepts the same model providers and the same AgentTemplate features as the built-in runtime. For the provider matrix, see Model provider support. For what an AgentTemplate can bind, see Tool and skill support.
Both kinds of agent receive the same variables.
| Variable | Contents |
|---|---|
KAGENT_CONFIG_JSON | The compiled agent configuration: the resolved system prompt, the model and its settings, and every tool, skill, and plugin that the AgentTemplate binds. |
KAGENT_AGENT_CARD_JSON | The agent card that kagent advertises for this agent, rendered as JSON. |
KAGENT_NAMESPACE and KAGENT_NAME | The AgentTemplate’s namespace and name, which kagent’s own helpers use to scope sessions. |
Anything in spec.env | Literal values and credentialRef lookups from the Harness, resolved before the Actor starts. |
Note
Agent Substrate accepts at most 32 environment variables on an Actor, and the compiled configuration counts toward that limit. A revision that exceeds it fails to compile rather than starting and misbehaving.
Configure a BYO Harness
A byo Harness takes the same fields as any other, minus the runtime settings block. spec.byo is an empty object, because the runtime has nothing to configure. The image holds the behavior.
kubectl apply -f - <<EOF
apiVersion: kagent.dev/v1alpha3
kind: Harness
metadata:
name: my-byo-harness
namespace: kagent
spec:
byo: {}
workload:
image: <your-registry>/my-agent@sha256:<digest>
command: ["/my-agent"]
env:
- name: PORT
value: "80"
substrate:
workerPoolRef:
name: kagent-default
snapshotPolicy:
location: gs://<your-bucket>/kagent/
allowedAgentTemplates:
selector:
matchLabels:
kagent.dev/harness: my-byo-harness
EOFReview the following table to understand this configuration. For the fields that every Harness shares, see Configure a Harness.
| Field | Required | Description |
|---|---|---|
byo | Yes | Selects this runtime. The object is always empty, and naming a second runtime alongside it is rejected. |
workload.image | Yes | Your image, pinned by sha256 digest. A tag alone is rejected, because a revision must be reproducible. |
workload.command | Yes | The entrypoint to run, up to 32 entries. Required for byo and optional for every other runtime. |
env | No | Set PORT here unless the image pins port 80 itself. |
An opaque agent’s AgentTemplate carries only the label that the Harness selects on, plus a description for the agent card.
kubectl apply -f - <<EOF
apiVersion: kagent.dev/v1alpha3
kind: AgentTemplate
metadata:
name: my-byo-agent
namespace: kagent
labels:
kagent.dev/harness: my-byo-harness
spec:
description: An agent that my own image implements.
EOFTo configure the agent from Kubernetes instead, add the fields that any AgentTemplate takes, and read KAGENT_CONFIG_JSON in the image. For what those fields mean, see Your first agent.
Build the image
The A2A contract is a gRPC service and a readiness endpoint, so any language with a gRPC server can satisfy it. kagent ships helpers for two of them. To build and run a minimal image end to end before writing your own, see Run your own agent image.
Tip
The Go helper produces smaller images, faster startup, and lower memory use than the Python one. Where both suit the agent that you are building, prefer Go.
github.com/kagent-dev/kagent/go/adk/pkg/app serves the A2A gRPC service, the readiness endpoint on 8081, and the agent card, given any type that implements a2asrv.AgentExecutor. The helper is framework-agnostic, so the executor is the only part that you write.
application, err := app.New(app.AppConfig{
AgentCard: a2atype.AgentCard{
Name: "my-agent",
Version: "v1",
Capabilities: a2atype.AgentCapabilities{Streaming: true},
},
Port: "80",
AppName: "my-agent",
Logger: logger,
}, myExecutor{})
if err != nil {
return err
}
return application.Run()Setting Port to 80 keeps this image working without a PORT variable on the Harness. Omitting it falls back to the PORT environment variable and then to a default of 8080, which kagent never dials.
For a complete executor, see go/core/test/byoa2a/main.go in the kagent repository.
Known limitations
- The A2A interface is fixed. kagent advertises
http://127.0.0.1:80over gRPC, andspec.byotakes no field to change the address, the port, or the protocol. An image that serves A2A over HTTP JSON-RPC alone cannot run on this runtime. - The
kagent-langgraphandkagent-crewaiadapters do not qualify yet. Both build a FastAPI application with A2A JSON-RPC routes and no gRPC server, so neither satisfies the contract as shipped. Running LangGraph or CrewAI underbyocurrently means serving A2A over gRPC yourself. - Long-term memory is unavailable. Memory is configured under
spec.kagent.memoryand wired only by thekagentruntime’s compiler. AbyoHarness has no equivalent setting. For what a BYO image would need to replace, see Agent memory. - A broken port mapping presents as a healthy agent. Readiness passes on 8081 regardless of the A2A service, so the AgentInstance reports
READYand every invoke fails. This is kagent#2758.