Our SWE-in-a-team benchmark research is ready.Read the results.
← Blog
||Guides

Run Codex headless with codex exec, on real tickets

Run Codex in an isolated sandbox that never holds your OpenAI key, with CI, review and QA around the run. Codex builders resolved 55 of 60 benchmark missions.

To run Codex headless on real tickets, add an OpenAI API key to SHIP and set harness: codex for the builder or the reviewer in ship.yml. Then assign an issue, or run ship delegate plan --harness codex. SHIP runs Codex in an isolated sandbox with no terminal attached and no key inside it. Your CI, a reviewer and QA check the work. In SWE-in-a-team, Codex builders resolved 55 of 60 missions.

Read what codex exec does on its own

OpenAI's non-interactive mode docs cover codex exec on its own. The command takes a task prompt as a single argument:

codex exec "summarize the repository structure and list the top 5 risky areas"

Progress streams to stderr. Only the final agent message prints to stdout, so you can pipe the result to a file or another command. Add --ephemeral when you do not want Codex to keep its session files on disk.

By default, codex exec runs in a read-only sandbox. --sandbox workspace-write allows edits. --sandbox danger-full-access grants broader access, and the docs say to use it only in a controlled environment. The older --full-auto flag is retired in favor of --sandbox workspace-write. --approve-for-me routes approval requests through automatic review with the workspace-write sandbox.

For machine-readable output, --json turns stdout into a JSON Lines stream that captures every event Codex emits, such as thread.started, turn.completed and item.*. The flag -o, or --output-last-message, writes just the final message to a file. --output-schema asks for a final response that matches a JSON Schema you give it.

On authentication, the docs say to set CODEX_API_KEY only for the one call that needs it. They warn against a job-level environment variable for OPENAI_API_KEY or CODEX_API_KEY in a workflow. They call an API key the right default for automation. For CI, they point to the Codex GitHub Action instead of a hand-written CLI step, because the action starts a secure proxy for the key. The GitHub repository's own exec reference adds no separate claim. It points back to the same non-interactive mode page.

That leaves a reader with a command and a set of flags. Three things are still to build: a container for the sandbox, a place for the key, and a check on what the run produced.

SHIP builds that part for you. A Codex stage works in an isolated sandbox. It runs your formatter, linter and tests before it pushes, and opens the pull request (how a mission runs). Each sandbox gets a scoped, single-use callback token. Before the agent reports back, SHIP stores the full session, the assembled prompt, the token usage and the cost. Nobody attaches to the run, and that record is what you read afterward.

Add an OpenAI API key

On the Connect your AI providers step, add an OpenAI credential. SHIP checks it with a live call when you save it. A typo or a key from the wrong account fails there, before your first mission (LLM credentials).

Every role defaults to the claude-code harness on Opus. So an Anthropic credential covers the Planner, the Builder, the Reviewer and QA out of the box. Add the OpenAI key to put Codex on one or more roles, as the next section shows.

The key never enters the sandbox. SHIP starts the agent's container with a placeholder and puts the real credential on the outbound request, outside the container. If SHIP does not replace the placeholder, the provider gets the placeholder and rejects the call.

Give the sandbox your toolchain

Every agent starts in a sandbox built on the platform base: an isolated container with Node, pnpm, git and the agent CLIs (Custom sandboxes). If that is all your repository needs, skip this step.

If Codex needs more, such as Python, a JDK or a system library, write a custom sandbox. Its definition is a short Dockerfile with no FROM line, because it always starts from the platform base:

.ship/sandbox.Dockerfile
RUN export DEBIAN_FRONTEND=noninteractive && apt-get update \
  && apt-get install -y --no-install-recommends python3 python3-venv python3-pip \
  && rm -rf /var/lib/apt/lists/*
ENV UV_LINK_MODE=copy

SHIP supports RUN, ENV, ARG and WORKDIR. It refuses COPY, ADD and USER, with the line and the reason. It builds the definition once, keeps a snapshot, and starts every mission from that snapshot. No mission pays to install the tools.

This answers the "docker" search behind this page. OpenAI's own pages describe no container image for codex exec. SHIP's custom sandbox is a Dockerfile format, not a Docker runtime you manage. You choose where each role runs with agents.<role>.sandbox in ship.yml, or with a .ship/sandbox.Dockerfile in your repository. Use agents.<role>.size for more CPU or memory, from standard-1 up to standard-4, which is the default. SHIP reads ship.yml and .ship/sandbox.Dockerfile from your default branch only, so a pull request cannot change the sandbox that reviews or tests it.

Put Codex on the builder or the reviewer

With no ship.yml, every role runs claude-code on opus. Add the file at the repository root to put Codex on a role instead:

ship.yml
# yaml-language-server: $schema=https://letsship.ai/schema/ship.yml.json
version: 1

deployments: {}

agents:
  builder:
    harness: codex

The roles you leave out keep their defaults. A model alias, codex or gpt, follows the newest model of its family. A versioned model id stays exactly where you put it (Schema).

SWE-in-a-team has numbers for the Codex builder. Three OpenAI models ran as the codex builder on the same 20 tickets, one run each, while the Planner, Reviewer and Tester stayed on claude-opus-4.8 on claude-code. A ticket counts as resolved when the mission reached Ready for Acceptance and hidden tests passed. The figures are recomputed from the per-run data, API-equivalent at list price.

Builder model on codexResolvedPer resolved ticketMedian mission time
gpt-5.6-sol20 of 20$3.8316.3 min
gpt-5.6-terra19 of 20$3.8818.5 min
gpt-5.6-luna16 of 20$3.0814.0 min

Together the three Codex builders resolved 55 of the 60 missions. The study is one trial per cell in one repository. Read the table as directional, not final.

To try Codex on one mission, with no change to the file, set an override:

ship mission override set SHIP-412 --role reviewer --harness codex

Hand Codex a ticket or a spec

Work reaches Codex from a tracker or from your terminal (how work reaches SHIP). On a Linear project, assign the issue to SHIP. On GitHub Issues, comment @SHIP from an account with write access to the repository. Codex on Linear covers the tracker setup.

From a terminal, ship delegate plan skips the Planner. The role you name builds from a spec you wrote:

ship delegate plan --title "Rate-limit the search endpoint" --file docs/plan.md --harness codex

If the change already exists on a branch, ship delegate pr --at reviewer hands the open pull request to a Codex reviewer instead. Codex as reviewer for Claude Code covers that setup.

You follow the mission with no terminal session attached to it. The command ship status SHIP-412 --json prints the stage, review state, test state, pull request and agent runs (Command reference). Use @SHIP pause, @SHIP resume and @SHIP stop on the issue to control the mission itself.

Cap what an unattended run can spend

SHIP limits an unattended mission in four ways. All four limits are on by default:

LimitDefaultWhen it is reached
Spend per mission$5The mission stops
Credits per mission2 credits, or 6 agent-hoursThe mission pauses until a member of your organization approves 1 more credit
Identical failures in a row at one gate2The mission shows Needs attention and the pull request stays open
Builder attempts in one mission25The same result, as an outer backstop

The spend limits are on the LLM credentials page. The loop limits sit under retries in ship.yml (Retry limits). If you raise only maxRetries, usually nothing changes, because a stuck mission hits an oscillation limit long before the ceiling.

SHIP prices every stage from the list rates of the model that served each call, not from what the harness reports about itself. Read the per-stage cost of your first few Codex missions before you move a role to a different model.

To run Claude Code instead, see Run Claude Code headless.

Questions

What is codex exec?

codex exec is the OpenAI Codex CLI command that runs a task from a single prompt argument with no interactive session. It streams progress to stderr. Only the final agent message prints to stdout, so you can pipe it to a file or another command.

How do I run codex exec in CI?

OpenAI's own docs set CODEX_API_KEY for the one invocation that needs it, never as a job-level environment variable. They recommend the Codex GitHub Action over a hand-written CLI step. In SHIP, no terminal is attached to Codex. A mission starts from an issue or a CLI hand-off, and SHIP stores the full session for you to read.

Can I run Codex in Docker?

OpenAI's codex exec docs do not describe a Docker image. In SHIP you do not build a container for Codex. To add tools, you write a custom sandbox as a short Dockerfile with no FROM line, and every mission reuses its snapshot.

Which credential does Codex need in SHIP?

SHIP runs the codex harness on an OpenAI API key. SHIP checks it with a live call when you save it, and never puts it inside the sandbox. An Anthropic credential still covers any role you leave on the claude-code default.

Which model should the Codex builder use?

On SWE-in-a-team's 20 tickets, the Codex builders gpt-5.6-sol, gpt-5.6-terra and gpt-5.6-luna resolved 20, 19 and 16 tickets. They cost $3.83, $3.88 and $3.08 per resolved ticket, and together resolved 55 of 60.

How much does a headless Codex mission cost?

SHIP records what every stage of a mission cost, priced from list rates for the model that served each call. A single mission stops at $5 by default, and pauses at 2 credits or 6 agent-hours until a member of your organization approves more.

Run Codex on your real tickets

See Codex build or review a ticket from your own backlog in an isolated sandbox. CI, review and QA check the change before the pull request reaches you.