kagent Codex Harness: Run OpenAI Codex on Kubernetes

Part of: Agent Runtimes

How to run OpenAI Codex as a kagent 1.0 harness: the four resources, the ModelConfig rules, MCP and env restrictions, and how to debug a Compatible=False agent.

My Claude Code harness post gets the questions. The follow-up is usually “can I do the same thing with Codex?” Yes. Here’s how, and where it bites.

Disclosure: I work at Solo.io, which created kagent (now an open-source project) and sells Solo Enterprise for kagent. This post is built from the public Solo Enterprise for kagent harness docs as of 2026-10-30. kagent 1.0 is alpha, so field names can still move.

TL;DR

  • spec.codex: {} on a Harness runs Codex as the agent loop, on Agent Substrate.
  • The ModelConfig must use openAI.apiFormat: responses, and the key must be entitled to the model.
  • MCP servers must be Streamable HTTP. No structured output, no token passthrough.

Why run Codex in the cluster

Codex on a laptop is one process that holds your key, your shell and your session. On kagent, the session runs in a sandboxed Actor that can suspend to object storage and resume, the model key sits behind the egress gateway instead of in the agent’s environment, and every edit to the agent produces a new digest-pinned revision. Sessions already running keep the revision they started with. More on that split in what a cloud-native agent harness is .

The four resources

ModelConfig (model + key), Harness (runtime + infra), AgentTemplate (behavior), Agent (the pairing). This is straight from the docs. It assumes the kagent-openai Secret from the OpenAI setup guide and a kagent-default WorkerPool.

apiVersion: api.kagent.dev/v1alpha3
kind: ModelConfig
metadata:
  name: codex-model-config
  namespace: kagent
spec:
  apiKeySecret: kagent-openai
  apiKeySecretKey: OPENAI_API_KEY
  model: gpt-5.2-codex
  provider: OpenAI
  openAI:
    apiFormat: responses
---
apiVersion: api.kagent.dev/v1alpha3
kind: Harness
metadata:
  name: codex-harness
  namespace: kagent
spec:
  codex: {}
  substrate:
    workerPoolRef:
      name: kagent-default
    snapshotPolicy:
      location: s3://ate-snapshots/kagent/
---
apiVersion: api.kagent.dev/v1alpha3
kind: AgentTemplate
metadata:
  name: codex-template
  namespace: kagent
spec:
  modelConfig:
    name: codex-model-config
  systemPrompt: You are a concise, helpful coding assistant.
---
apiVersion: api.kagent.dev/v1alpha3
kind: Agent
metadata:
  name: codex-agent
  namespace: kagent
spec:
  template:
    ref:
      name: codex-template
  harnessRef:
    name: codex-harness

I left workload.image off the Harness on purpose. Omit it and you get the digest-pinned Codex image that ships with your kagent release. Override it per Harness, or install-wide with config.harnessImages.codex in Helm. Either way it must be pinned by sha256.

Gotchas

GotchaWhat happens
ModelConfig not apiFormat: responsesRejected. Only baseUrl is allowed alongside it
Key not entitled to the modelTurn fails with Codex execution failed
BedrockOnly OpenAI gpt-* model IDs, only region setting, and an AWS_BEARER_TOKEN_BEDROCK key (IAM keys rejected)
SSE MCP serverRejected. Use STREAMABLE_HTTP
tokenPassthrough, defaultHeaders, tlsRejected
Structured outputRejected
Setting CODEX_HOME, OPENAI_API_KEY or AWS_* creds in spec.envEvery Agent on that Harness goes Compatible=False
SubagentsCan’t carry their own tools or skills, and must share provider + creds
File attachments in UI chatkagent harness only

The env one catches people who try to “just pass the key.” Don’t. Keys belong in the ModelConfig Secret, and the gateway injects them.

Check it

kubectl describe agent codex-agent -n kagent
kubectl get agent codex-agent -n kagent -o jsonpath='{.status.workloadImage}'

ResolvedRefs false usually means a missing WorkerPool. Compatible false means one of the gotchas above. Ready false means Substrate capacity isn’t up yet. The Harness itself never reports status, so stop staring at kubectl get harness.

Telemetry

With tracing on and captureContent: true, Codex prompts and replies land on the invoke_agent span, and tool arguments and results land on execute_tool. maxCaptureBytes (default 16384, max 65536) caps each one. Default is off, and that’s right: anyone who can read your traces can read what you capture. To flip it for just this Harness, set OTEL_INSTRUMENTATION_GENAI_CAPTURE_MESSAGE_CONTENT to SPAN_ONLY or NO_CONTENT in spec.env.

Codex vs Claude harness

codexclaude
ProvidersOpenAI (responses), Bedrock gpt-*Anthropic, Bedrock
MCPStreamable HTTP onlyBinds whole server, ignores subsets
Raw API body captureNoYes (captureRawApiBodies)
Structured outputNoNo

All four side by side: kagent harnesses compared .

Frequently asked questions

Can kagent run OpenAI Codex?

Yes. A kagent Harness with the codex block runs Codex as the agent’s loop. It works with OpenAI models through a ModelConfig that sets openAI.apiFormat to responses, or with Bedrock using OpenAI gpt-* model IDs.

Why does my kagent Codex agent fail with 'Codex execution failed'?

The most common cause is an API key that isn’t entitled to the model named in the ModelConfig. The key must have access to that exact model.

Does the kagent Codex harness support SSE MCP servers?

No. A RemoteMCPServer used by a codex agent must use the STREAMABLE_HTTP protocol. SSE is rejected, and so is tokenPassthrough.

What's the difference between the kagent Codex and Claude harnesses?

Codex runs against OpenAI or Bedrock gpt-* models and needs Streamable HTTP MCP servers. Claude runs against Anthropic or Bedrock and binds every tool on an MCP server even if you list a subset. Neither supports structured output, tokenPassthrough, or ModelConfig defaultHeaders or tls.

Disagree or have a war story? Reply on X