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

Run Claude Code headless on real tickets, in a sandbox

Run Claude Code without a terminal in an isolated sandbox that never holds your key, with CI, review and QA around it. 77 of 80 benchmark missions resolved.

To run Claude Code headless on real tickets, add an Anthropic API key or subscription credential to SHIP and assign an issue to it. SHIP runs the claude-code harness for every role by default. Each stage works in an isolated sandbox with no terminal attached and no key inside it. Your CI, the Reviewer and QA check the Builder's work. You start a mission from an issue or with ship delegate plan, and read the stored session afterwards. In SWE-in-a-team, claude-code builders resolved 77 of 80 missions.

Compare Claude Code's own headless and sandbox options

Claude Code gives you the two parts of an unattended run, and leaves you to put them together. Anthropic's headless docs cover the headless half:

  • claude -p "...", or --print, runs Claude Code non-interactively and exits with a non-zero code when the run fails.
  • --bare skips the auto-discovery of hooks, skills, plugins, MCP servers, auto memory and CLAUDE.md. The docs call it "the recommended mode for scripted and SDK calls". In bare mode Claude Code reads ANTHROPIC_API_KEY, because it "doesn't use your subscription login".
  • --allowedTools and --permission-mode (acceptEdits, dontAsk or auto) decide what runs without a person to approve it.
  • --output-format json returns the result with total_cost_usd, which the docs describe as client-side estimates that can differ from your bill.

The sandbox docs cover the other half. The Bash sandbox is off by default, and you turn it on with /sandbox or sandbox.enabled. It uses Seatbelt on macOS and bubblewrap with socat on Linux and WSL2. It wraps shell commands only. Claude's file tools, hooks and MCP servers run outside it.

Two defaults matter for an unattended run. By default, sandboxed commands can read most of the machine, and the docs name credential files such as ~/.ssh and ~/.aws/credentials. They also get the environment variables of Claude Code, with any secrets in them. For one boundary around everything, Anthropic's advice is to "run the Claude Code process itself in a container, virtual machine, or the sandbox runtime". That container, the key inside it, the output and who checks it are then yours to build.

SHIP does that part for you. Its Builder works in an isolated sandbox, 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 afterwards.

Add an Anthropic key or subscription

On the Connect your AI providers step, add an Anthropic credential. An API key works, and so does a subscription 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).

One credential is enough. The Planner, the Builder, the Reviewer and QA all default to the claude-code harness on the opus alias, so a single Anthropic credential runs a complete mission.

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. The platform base is an isolated container with Node, pnpm, git and the agent CLIs (Custom sandboxes). If that is all your repository needs, skip this step.

If Claude Code 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, and 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, so no mission pays to install the tools.

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. Credentials for a private package registry go on the outbound request too, so they never enter the sandbox, its build log or its snapshot.

SHIP reads ship.yml and .ship/sandbox.Dockerfile from your default branch only. A pull request therefore cannot change the sandbox that reviews or tests it.

Pin a model and effort to each role

With no ship.yml, every role runs claude-code on opus. To choose per role, add the file at the repository root:

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

deployments: {}

agents:
  builder:
    harness: claude-code
    model: sonnet
    effort: high

The roles you leave out keep their defaults. A model alias (opus, sonnet or haiku) follows the newest model of its family, and a versioned model id stays exactly where you put it. On claude-code, effort must be low, medium, high, xhigh or max, or the stage fails before it starts (Schema).

SWE-in-a-team has numbers for the builder. Four Claude models ran as the claude-code builder on the same 20 tickets, one run each, while the Planner, Reviewer and Tester stayed on claude-opus-4.8. 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 claude-codeResolvedPer resolved ticketMedian mission time
claude-sonnet-520 of 20$4.3120.7 min
claude-opus-520 of 20$5.4823.0 min
claude-fable-519 of 20$6.4420.7 min
claude-haiku-4-518 of 20$4.2322.7 min

The most expensive model did not resolve the most tickets. The Fable and Haiku misses passed CI, review and the Tester, and only the hidden tests caught them. The study is one trial per cell in one repository, so read the table as directional.

To try a different model on one mission without a change to the file, pass it on the hand-off:

ship delegate plan SHIP-412 --file docs/plan.md --harness claude-code --model opus

Hand Claude Code a ticket or a spec

Work reaches Claude Code 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. The Planner writes the plan from the issue and the repository, and the Builder works from that plan. Claude Code on Linear covers the tracker setup.

From a terminal, ship delegate plan skips the Planner and has the Builder work from a spec you wrote:

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

If the change already exists on a branch, ship delegate pr hands the open pull request to the Reviewer and QA instead.

You follow the mission without a terminal session attached to it. The command ship status SHIP-412 --json prints the stage, the review and test state, the pull request and the agent runs (Command reference). To change course, ship instruct SHIP-412 -m "..." sends the agents back to the open pull request with your instruction. @SHIP pause, @SHIP resume and @SHIP stop on the issue control the mission itself.

Cap what an unattended run can spend

SHIP limits an unattended mission in four ways, and 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, and 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. A role on a larger model can cost about 5 times as much. Read the per-stage cost of your first few Claude Code missions before you move a role up a model size.

To run an open-weight model as the builder instead, the same setup works with a different harness: see Run the pi coding agent headless.

Questions

What is Claude Code headless mode?

Claude Code headless mode is the non-interactive run you start with claude -p, or --print, and a prompt. Anthropic recommends --bare for scripts and CI. In SHIP, no terminal is attached to Claude Code. A mission starts from an issue or a CLI hand-off, and SHIP stores the full session for you to read.

How do I sandbox Claude Code?

Claude Code has a Bash sandbox that is off by default and covers shell commands only. Anthropic suggests a container or virtual machine for one boundary around everything. SHIP runs each Claude Code stage in an isolated sandbox that never holds your Anthropic key and is gone when the stage finishes.

Can I run Claude Code in Docker?

You can run Claude Code in a container yourself, which Anthropic's sandbox docs suggest for one boundary around the whole process. In SHIP you do not build that container. To add tools, you write a custom sandbox as a short Dockerfile with no FROM line, and every mission reuses its snapshot.

Can headless Claude Code use my Claude subscription?

Claude Code's bare mode does not use a subscription login and needs ANTHROPIC_API_KEY. SHIP accepts an Anthropic subscription credential in addition to an API key. SHIP verifies it with a live call when you save it, and never puts it inside the sandbox.

Which model should the Claude Code builder use?

In SWE-in-a-team, claude-sonnet-5 and claude-opus-5 on claude-code each resolved all 20 tickets. They cost $4.31 and $5.48 per resolved ticket. claude-fable-5 resolved 19 at $6.44, and claude-haiku-4-5 resolved 18 at $4.23.

How much does a headless Claude Code 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 Claude Code on your real tickets

See Claude Code build a ticket from your own backlog in an isolated sandbox. Review and QA check the change before the pull request reaches you.