Skip to content

Run a Codex agent

The Codex runtime replaces the PromptKit runtime container with one that drives the Codex CLI. The agent’s inference is billed to an OpenAI API key or, signed in with ChatGPT, to one person’s ChatGPT plan. Every session the agent serves uses that one credential.

It works like the Claude Code runtime: the facade, sessions and exposure work as for any agent, and the CLI’s tools are the agent’s ToolRegistry tools, run the same way as for a PromptKit agent. Codex’s own shell, file, web search, browser and image features are off, so nothing runs on the runtime pod.

  • An OpenAI API key, or the Codex CLI on a machine signed in to a ChatGPT plan.
  • A PromptPack for the agent’s promptPackRef.

With an API key, store it in a Secret in the agent’s namespace:

Terminal window
kubectl create secret generic codex-credential --from-literal=api-key=<key>

To bill a ChatGPT plan instead, sign in on a machine with codex login, and store the file it writes:

Terminal window
kubectl create secret generic codex-credential --from-file=auth.json=$HOME/.codex/auth.json
apiVersion: omnia.altairalabs.ai/v1alpha1
kind: AgentRuntime
metadata:
name: codex
spec:
promptPackRef: { name: codex-pack }
framework:
type: custom
image: ghcr.io/altairalabs/omnia-codex-runtime:main
facades:
- type: websocket
runtime:
replicas: 1
extraEnv:
- name: OPENAI_API_KEY
valueFrom:
secretKeyRef: { name: codex-credential, key: api-key }
volumes:
- name: cli-home
emptyDir: {}
volumeMounts:
- { name: cli-home, mountPath: /home/codex }

With a ChatGPT sign-in, replace OPENAI_API_KEY with CODEX_AUTH_JSON taken from the auth.json key. The runtime writes it where Codex looks for it when it starts.

Put the credential and the volume under spec.runtime. It applies to the runtime container only. podOverrides applies to the facade container too, and would expose the credential to it.

Keep replicas: 1 and leave autoscaling off. A session’s CLI process lives in the pod that started it.

The cli-home volume is required. The runtime container’s root filesystem is read-only, and Codex keeps its own state under /home/codex. Without the volume the runtime exits at startup with a message naming it.

To choose the model, add OMNIA_CLI_MODEL to extraEnv. Unset uses Codex’s default.

Reference a ToolRegistry, exactly as for a PromptKit agent:

spec:
toolRegistryRef: { name: codex-tools }

Codex sees the tools under their own names, grouped as an MCP server named omnia. Each call goes through the same executor as a PromptKit agent’s, so ToolPolicy decisions, injected headers, credentials and delegation apply to it. Calls appear on the session’s tool calls in the dashboard. A handler with a sandbox block, on an AgentRuntime with spec.sandbox enabled, gives each session its own sandbox, as for a Claude Code agent.

The runtime gives Codex the pack’s prompt as developer instructions. The pack’s skills are listed to Codex by name and description. Codex’s shell is off, so it cannot open a skill’s files itself. Instead, the runtime serves them read-only as resources of the omnia MCP server, at skill://<skill name>/<path>. It also tells Codex to read a skill’s SKILL.md before using the skill. An agent with skills and no ToolRegistry works the same way.

Files in a skill’s subfolders are served under their relative path, so a SKILL.md that points to refs/checklist.md works. Codex can read a skill’s scripts but cannot run them; make anything the agent must run a ToolRegistry tool.

Each skill serves at most 200 files, and files over 1 MiB are left out. The runtime logs each file it leaves out when it starts. A changed pack takes effect when the agent’s pods roll out.

When Codex needs a decision it asks, and the question appears in the dashboard console as a card, as for a Claude Code agent. Over A2A, from a function call or on a schedule, Codex is told nobody is available and proceeds on its best judgment.

Limit concurrent sessions and keep conversations

Section titled “Limit concurrent sessions and keep conversations”

The concurrency limit (OMNIA_CLI_MAX_CONCURRENT, default four), idle stopping and resuming all work as for a Claude Code agent. So does resuming after a pod restart with a Redis context store; see Keep conversations across pod restarts. A session resumes its Codex thread.

Terminal window
kubectl get agentruntime codex -o jsonpath='{.status.runtimeCapabilities}'

The output is ["invoke","client_tools"]. Open the agent in the dashboard console and send a message; the reply streams as it is written.

The runtime records token counts on each message with a cost of 0. Each turn is also recorded as a provider call with the source codex-notional and Codex’s token counts. Codex reports no price, so its cost is 0 and cost totals leave it out.

SymptomCause
Runtime container exits with HOME ... is not writableThe cli-home volume is missing from spec.runtime.
Runtime container exits with codex CLI is X but this runtime was built against YThe image’s CLI was replaced. Use the published image.
Runtime container exits with CODEX_AUTH_JSON is not JSONThe Secret holds something other than Codex’s auth.json.
A reply fails with codex CLI turn failedCodex reported the turn failed; the message names its error, for example an invalid credential.
A conversation restarts from scratch after a pod restartThe agent has no Redis context store.
The agent says it has no toolsThe runtime logs tool bridge init failed at startup when the ToolRegistry’s tools could not be loaded.

See Authoring a custom runtime for the runtime contract this image implements.