Self-hosted environments
Run Claude Code cloud sessions on runners inside your own network: how environments, runners and sessions fit together, and when to self-host.
A self-hosted environment lets your organisation run Claude Code cloud sessions on machines it operates. Developers still start sessions from claude.ai, the desktop and mobile apps, claude --cloud or a routine, and the experience is the same. The difference is where the work happens: instead of an Anthropic-hosted VM, the session runs on a runner inside your network.
Note: Self-hosted environments are in public beta for Team and Enterprise plans and are off by default. See availability before you plan anything.
If your developers only use Claude Code in a terminal or IDE, there is nothing to set up here; those sessions already run on their own machines. If you just want to drive your own always-on box from your phone, Remote Control is simpler and also works on Pro and Max.
Ready to build one? Go to the quickstart. Reviewing security first? Start with deploying to production.
The moving parts
Three things make up a self-hosted setup:
| Part | What it is | Where it lives |
|---|---|---|
| Environment | A named destination for cloud sessions that groups a set of runners | Created on the Cloud environments page in claude.ai admin settings |
| Runner | A process that claims and executes sessions, much like a self-hosted CI runner | Your hosts, containers or pods |
| Session | One Claude Code task a person (or a Claude Tag agent) started | A child Claude Code process the runner spawns |
Two more terms come up constantly:
- Environment secret: the one shared credential runners use to register with the environment. It is shown once, when the environment is created, and the admin UI calls it the environment key.
- Owner: the identity a session belongs to. More on this under runner ownership.
In API fields, token claims and metric names, an environment is called a pool and its ID is the pool_id (values look like ccpool_...). The reference maps both spellings, including deprecated pool flag names.
How a session reaches your runner
- A developer starts a cloud session and picks your environment in the environment picker, which lists your organisation's self-hosted environments next to the Anthropic-hosted ones.
- Anthropic's control plane puts the session on your environment's queue.
- A runner with spare capacity claims it, clones the chosen repository using git credentials you configure, and launches a Claude Code child process on the host.
- The child process streams events back to Anthropic over HTTPS and talks to your internal services and git host directly from inside your network.
Every connection is outbound. Anthropic never connects into your network. You can run runners permanently, or run the autoscaling orchestrator, a second process you host that starts runners as sessions queue up. Either way, you set up the environment once and it appears on every supported surface.
Availability and limitations
| Area | Current position |
|---|---|
| Plans | Public beta on Team and Enterprise. An Owner turns on Allow self-hosted environments on the Cloud environments admin page, which needs cloud sessions enabled for the organisation |
| Zero Data Retention and HIPAA | Not available to organisations with Zero Data Retention or the HIPAA configuration |
| Model inference | Anthropic API by default. A runner can send model requests to Amazon Bedrock or Google Cloud's Agent Platform instead, but session content still goes to Anthropic, and on such a runner server-managed settings and claude.ai organisation policies don't reach sessions |
| Surfaces | claude.ai/code, mobile and desktop apps, routines, and the terminal via claude --cloud or an --environment dispatch. Claude Tag sessions can run here but can't use Access bundles yet. Claude Security and Code Review don't route here yet |
| Repositories | Checked out from GitHub. GitHub Enterprise Server has its own network requirements |
| Billing | Same Claude Code usage consumption as Anthropic-hosted sessions |
The deploy guide also lists known issues; read it before rolling out widely.
When self-hosting is worth it
Honestly, most teams should stay on Anthropic-hosted cloud environments. They need no infrastructure. Self-hosting means you build and maintain the runner image, run the fleet and own its network.
It earns its keep when one of these is true:
- Your code needs your network. Sessions can hit internal APIs, databases and private registries without exposing them to the internet.
- Your toolchain is unusual. Bake compilers, SDKs and internal CLIs into the runner image so every session starts ready to build.
- Compliance wants checkouts kept in-house. Repository checkouts and build artefacts stay on your machines. Be clear with your security team, though: the conversation still goes to
api.anthropic.com.
The bigger hardware ceiling is a side benefit too: Anthropic-hosted VMs are roughly 4 vCPUs and 16 GB of RAM.
Runner ownership
A runner serves one owner at a time. The first session it claims locks it to that session's owner, and from then on it only runs that owner's sessions, up to its --capacity. This keeps one person's checked-out code from ever sitting next to another's on the same disk.
Who counts as the owner:
- Sessions a person starts: their user account.
- Claude Tag channel sessions: these run without a user account, so the owner is the Claude Tag agent that started them. Every channel session from that agent has the same owner, regardless of who posted in Slack. A runner locked to that agent with
--capacityabove one, or a positive--drain-grace-sec, will therefore run sessions that different people triggered.
So your minimum fleet size equals the number of owners you expect to be active at the same time, counting both people and Claude Tag agents.
Session lifecycle
Once a session is on the queue:
- A runner with free capacity claims it and takes a lease.
- The runner clones the repository into its working directory and starts the child Claude Code process.
- The child streams events over HTTPS. Meanwhile the runner keeps polling; each poll renews the lease and acts as a heartbeat.
- If polling stops, the lease lapses after about 60 seconds and the server requeues the session for another runner within a few minutes.
Each poll request gets 10 seconds. If one times out, gets lost or returns something the runner can't parse (an intercepting proxy returning its own HTML page is the usual culprit), the runner keeps its live sessions going and retries after a second or two. Repeated failures double the gap each time, capped at 20 seconds, and the gap shrinks again when the lease is close to expiring.
Runner lifecycle
After locking to an owner, a runner keeps claiming that owner's queued sessions while it has active ones and hasn't been told to stop. When they finish, --drain-grace-sec decides what happens:
0(default): the runner exits immediately. Your orchestrator (Kubernetes, for example) restarts it with a clean disk, ready for any owner.- A positive number: it keeps polling the same owner's queue for that many seconds before exiting.
That exit-and-restart pattern is what isolates owners without the runner having to wipe its own disk.
Stopping runners cleanly
How your platform kills hosts decides whether you need --retire-at:
- It sends
SIGTERMwith a reasonable grace period. No flag needed. The runner drains, or with--defer-shutdown-max-minkeeps serving the sessions it already holds. - It destroys hosts at a known time without a signal, or with too little grace (sandbox lifetime caps, spot reclamation). Pass
--retire-at <epoch-seconds>set a few minutes before that moment.
At the retire time the runner stops taking work and releases each active session via the same path --release-idle-session-min uses, so the session resumes on a fresh runner when the user next sends a message:
- A session that is mid-turn is released once the turn ends, after waiting (no longer than
SELF_HOSTED_RUNNER_POST_TURN_SETTLE_MS) for the process to report the turn's end to Anthropic. Runners before v2.1.280 released as soon as the turn finished. - If a turn ends with background tasks still running, the runner waits up to 60 seconds for them, then releases anyway. If the tasks finished but the follow-up turn that reads their output hasn't run, it holds the session until that turn completes, waiting at most
SELF_HOSTED_RUNNER_BG_RESULT_GRACE_MSfor it to start.
When everything is released, the runner exits with status 0. A turn still running when the host dies is lost regardless. Without --retire-at, a silent kill looks like a crash: the control plane records a lost worker and requeues the session.
Network paths
| Connection | From | To | Notes |
|---|---|---|---|
| Control plane | Runner | api.anthropic.com | Outbound HTTPS polling, plus setup-progress and failure events. Polling is the heartbeat |
| SCM connector | Orchestrator | Anthropic | Optional; the only WebSocket connection |
| Git | Runner and session | Your git host | HTTPS or SSH with credentials you provide, or optionally the Anthropic git proxy via api.anthropic.com |
| Session stream and inference | Session child | api.anthropic.com | Event stream plus model calls. Inference uses an Anthropic-issued, session-scoped OAuth token unless you route to Bedrock or Agent Platform |
The deploy guide's network requirements list every host a session may need.
Corporate egress proxies work. Set HTTPS_PROXY, NO_PROXY and the mTLS variables described in network configuration in the runner's and orchestrator's environment. They cover control-plane calls, the SCM connector WebSocket and the built-in clone over HTTPS, and sessions inherit them. Because session streaming uses server-sent events, the proxy must not buffer responses. If it needs a Proxy-Authorization header, the runner can add one; the deploy guide explains how.
What stays on your side
| Stays on your infrastructure | Goes to Anthropic |
|---|---|
| Repository checkouts | Prompts, responses and tool results |
| Build artefacts | The stored session transcript, so sessions can be resumed elsewhere |
| Secrets on the host | Queueing and orchestration state |
| Files a session creates or edits | The claude.ai interface itself |
The control plane stays Anthropic-hosted. Self-hosting moves execution, not orchestration, and routing inference to Bedrock or Agent Platform doesn't change that: the conversation still travels to api.anthropic.com in the event stream.
Where to go next
- Quickstart: one runner, one test session.
- Deploy to production: hardening, egress, git credentials, Kubernetes and Compose, troubleshooting.
- Customise sessions: wrapper scripts, lifecycle hooks, on-demand runners, MCP servers, permissions.
- Test end to end: a CI smoke test for runner images.
- Reference: every flag, variable, metric and the health endpoint.
- Verify session identity: let internal services trust session tokens.