Point OpenAI Codex at your own Macs. One provider block in config.toml, no model map to maintain, and the same fleet routing that serves Claude Code. Verified end to end on both the CLI and the desktop app.
Add this to ~/.codex/config.toml, then run codex:
model_provider = "herd"
[model_providers.herd]
name = "Ollama Herd"
base_url = "http://localhost:11435/v1"
wire_api = "responses"
No model map needed. Herd auto-routes whatever model id Codex sends to the best coding model you actually have loaded. Pull a model and go:
ollama pull qwen3-coder:30b
The same config block also works for the Codex desktop app, which reads the same file. See Quickstart if the herd is not running yet.
If you tried pointing Codex at Ollama, LM Studio, or any OpenAI-compatible server and it failed, this is why: Codex removed Chat Completions support in February 2026. wire_api = "responses" is the only valid value now, and it is the default.
That breaks most advice you will find. A server that implements only /v1/chat/completions cannot serve Codex at all, no matter how OpenAI-compatible it is otherwise. Guides still showing wire_api = "chat" predate the change.
Ollama Herd implements the Responses API directly at /v1/responses, so there is no translating gateway in the middle. That is what the wire_api = "responses" line above is telling Codex.
If you are appending to an existing ~/.codex/config.toml, model_provider = "herd" must go above the first [table] header. TOML assigns a bare key to whatever table precedes it, so pasting it at the bottom silently turns it into desktop.model_provider. Codex never sees it, no error is raised, and it quietly keeps using the default provider.
model_provider = "herd" # first line, before ANY [section]
[some.existing.section]
...
[model_providers.herd] # a table header, so this can live anywhere
No extra setup. The macOS desktop app reads the same ~/.codex/config.toml as the CLI, so the one provider block above serves both surfaces. Configure it once, and the app routes to your fleet the next time you open it.
Three things behave differently in the app, all verified against a real conversation:
/v1/models against its own undocumented schema, so the picker can fail to populate. This is cosmetic. Inference is unaffected, every turn still routes to your fleet, and the app carries its own model list regardless. Set the model in config.toml rather than the picker.-m flag. Where the CLI takes codex -m qwen3-coder:30b, the app relies on config plus auto-routing. Pin a model with FLEET_ANTHROPIC_MODEL_MAP if you want a specific one every time.Everything else on this page, the provider block, the TOML gotcha, model resolution, and troubleshooting, applies identically to both surfaces.
Plenty of projects document Codex on local models. This one was run against a real client on a real fleet, and the findings are published rather than asserted.
provider: herd, model id gpt-5.6-sol auto-routed to qwen3-coder:30b, x-fleet-served-model set on the response, traced as original_format='responses'.qwen3-coder:30b.gpt-5-codex, gpt-5.6-sol, and gpt-5.6-luna. That last one fired for chat title generation. A hand written map would have had to guess it existed, which is exactly the fragility auto-routing removes.qwen3-coder:30b and gpt-oss:120b both drove the full loop. File editing and file creation both work.Then a 26 hour soak across every code change in the release: 11,925 requests at 99.85% success, including 302 requests to /v1/responses with zero failures. The 18 failures all trace to a known slowness issue with one specific model, not to the Codex path.
Verification also found bugs unit tests had not, including tool calls being dropped in both directions and a model-listing schema that failed Codex's whole decode. Those are fixed. Spec-complete is not the same as client-verified.
Three things make the difference between a loop that finishes and one that wanders, all measured on local models:
qwen3-coder:30b exploring until it exhausted its budget. The same task phrased as "use the apply_patch command to fix X, then re-run pytest" succeeded.Budget for tokens too: a one-line fix cost about 109K tokens end to end, because agentic loops resend the whole conversation each turn and Codex's system prompt alone is around 27 KB.
Codex sends a model id such as gpt-5-codex. Herd resolves it in this order:
FLEET_ANTHROPIC_MODEL_MAP entry for that id wins.qwen3-coder:30b), it is used as-is. This is why codex -m qwen3-coder:30b works.404 telling you to pull a model.To pin one id without affecting anything else:
export FLEET_ANTHROPIC_MODEL_MAP='{"gpt-5-codex": "qwen3-coder:30b"}'
To disable auto-routing and require an explicit map, set FLEET_ANTHROPIC_AUTO_ROUTE=false.
gemma3:27b. If you have no vision model, ollama pull gemma3:4b is about 6 GB./v1/messages.X-Fleet-Served-Model tells you exactly which model and node answered.Codex calls tools constantly, so tool-use quality matters more than chat quality. Coding-tuned models with a real tool-call parser hold up; general chat models drop calls and hallucinate arguments.
We verified with qwen3-coder:30b (about 19 GB, 256K context). Newer coding families have since landed in Ollama's library: Qwen 3.6, Gemma 4, GLM-5, Kimi K2 Code, DeepSeek V4. See the current tool-capable list.
You do not need to tell Herd which one you picked. Auto-routing sends Codex to the best coding model you have loaded, so upgrading is ollama pull and nothing else.
mlx: models for Codex so you always land on a working Ollama-backed model. An explicit mlx: mapping returns a clear 503. Claude Code's /v1/messages does serve MLX.previous_response_id is rejected with a 400. Codex's default stateless mode, resending the conversation each turn, is what is supported and what it actually does.web_search, file_search, and MCP tool items have no local equivalent. Function tools pass through normally./v1/models against its own undocumented schema rather than OpenAI's. This is cosmetic, inference is unaffected, and every turn routes normally. Specify the model with -m or in config rather than the picker.Model metadata for <name> not found. Harmless: tool calling, editing, and multi-turn all work anyway.404 not available on any node. You have not pulled a chat or coding model, or the one you pinned is not on the fleet. Run ollama pull qwen3-coder:30b, or check curl localhost:11435/v1/models.
Codex errors about the wire protocol. Confirm wire_api = "responses" in ~/.codex/config.toml. wire_api = "chat" was removed from Codex in February 2026.
400 previous_response_id not supported. Your client is using server-side conversation state. Stateless mode, which Codex uses by default, is what Herd supports.
Codex ignores your config entirely. Check that model_provider = "herd" sits above the first [table] header. See the config mistake above.
Tool calls come back as plain text. The model is too small or not coding-tuned. Switch to one of the recommended models above.