Two ways to run the same agent
A Claude Managed Agent always has its model calls served by Anthropic. What varies is where the agent's tools actually execute. An environment can be type: cloud, an Anthropic-provisioned sandbox, or type: self_hosted, a sandbox running on infrastructure you control while Anthropic still orchestrates the model and the conversation.
This is not a minor deployment detail. It changes what resources a session can attach, how memory stores behave, what your compliance story looks like, and how much setup work lands on you before an agent can run its first tool call.
| Aspect | Cloud environment | Self-hosted sandbox |
|---|---|---|
| Tool execution | Anthropic-managed sandbox | Your own infrastructure |
| Network reach | Anthropic's egress controls | Your network policy |
| File and GitHub mounting | Managed by Anthropic | Not supported at all |
| Memory stores | Mounted by Anthropic at /mnt/memory/ | Downloaded and synced by an SDK worker you run |
| Lifecycle | Managed by Anthropic | Managed by you |
What a self-hosted sandbox actually is
Model access stays cloud-based either way: tool inputs and outputs still flow to Anthropic's control plane, where Claude reads them and decides what to do next. What moves is execution. In a self-hosted sandbox, an environment worker, a process you run, polls Anthropic's work queue for claimed sessions, downloads the agent's skills, executes each tool call locally on your infrastructure, and posts the results back.
This suits agents that need to operate on data that cannot leave your network boundary, reach internal services that are not publicly routable, or run under your organisation's own compliance and audit controls, rather than Anthropic's.
What you give up: file and GitHub resources
Self-hosted sessions cannot include file or github_repository resources. If you try, the API returns a 400:
Environment env_... is a self-hosted environment.
`resources` are not supported with self-hosted environments.
Two concrete consequences follow. First, repository skill discovery, which scans a mounted repository's .claude/skills directory at session start, only works in cloud sandboxes. A self-hosted agent that needs skills has to attach them through the agent's skills array instead, uploaded ahead of time, rather than pulled live from a repo. Second, there is no native way to hand a session a specific file at creation.
The workaround: metadata plus a staging script
Pass file references, an S3 path, a commit SHA, whatever your storage layer uses, through the session's metadata field instead:
curl https://api.anthropic.com/v1/sessions \
-H "x-api-key: $ANTHROPIC_API_KEY" \
-H "anthropic-beta: managed-agents-2026-04-01" \
-H "content-type: application/json" \
-d '{
"agent": "'"$AGENT_ID"'",
"environment_id": "'"$ANTHROPIC_ENVIRONMENT_ID"'",
"metadata": {"input_file": "s3://my-bucket/data.csv"}
}'
Your own environment worker's spawn script retrieves the session, reads metadata, and stages the actual file into the working directory before tool execution starts. This is genuinely a workaround, not a native resource type, and it means every self-hosted deployment needs a bit of custom staging logic that a cloud environment gets for free.
Setting up a self-hosted environment
1. Create the environment
Through the Console (Workspace > Environments > New > Self-hosted), the API, or an SDK:
client = anthropic.Anthropic()
environment = client.beta.environments.create(
name="self-hosted",
config={"type": "self_hosted"},
)
print(environment.id)
2. Generate an environment key
Open the environment in the Console and select Generate environment key, a Console-only action, then export both values where your worker runs:
export ANTHROPIC_ENVIRONMENT_KEY="sk-ant-oat01-..."
export ANTHROPIC_ENVIRONMENT_ID="env_..."
3. Prepare the worker host
The worker host needs to be Linux, with /bin/bash at exactly that path, and either the ant CLI or an Anthropic SDK (Python, TypeScript or Go) installed. If you plan to use memory stores, prepare a writable mount point ahead of time:
sudo mkdir -p /mnt/memory && sudo chown "$USER" /mnt/memory
4. Run an environment worker
Two deployment patterns:
- Always-on: a long-running process continuously polling the work queue. Needs only outbound HTTPS, nothing inbound.
- Webhook-triggered: a process that wakes on
session.status_run_startedevents instead of polling continuously. Needs an inbound webhook endpoint.
An always-on worker with the Python SDK:
import asyncio
import os
from anthropic import AsyncAnthropic
from anthropic.lib.environments import EnvironmentWorker
async def main() -> None:
environment_key = os.environ["ANTHROPIC_ENVIRONMENT_KEY"]
environment_id = os.environ["ANTHROPIC_ENVIRONMENT_ID"]
async with AsyncAnthropic(auth_token=environment_key) as client:
worker = EnvironmentWorker(
client,
environment_id=environment_id,
environment_key=environment_key,
workdir="/workspace",
)
await worker.run()
asyncio.run(main())
Memory stores: supported, but SDK-only
Self-hosted sessions can attach up to 8 memory stores, the same ceiling as cloud, but only through the SDK's EnvironmentWorker. The ant CLI's ant beta:worker poll command does not mount memory stores at all, so if a workload needs them, plan for the SDK worker from the start rather than discovering the gap mid-project.
When a work item is claimed, the worker downloads each attached store to /mnt/memory/<store_name>/, reconciles changes with the server on an interval (15 seconds by default), and on a graceful shutdown uploads final changes and removes the local directories:
worker = EnvironmentWorker(
client,
environment_id=environment_id,
environment_key=environment_key,
workdir="/workspace",
memory_sync_interval=10, # seconds, default 15
memory_sync_deletions="log_only", # "enabled", "log_only", or "disabled"
)
Constraints worth knowing before you rely on this in production:
- One session per store per host. Two sessions cannot mount the same memory store simultaneously.
- Shutdown must be graceful. Send
SIGTERMorSIGINT, notSIGKILL, so the worker has a chance to sync before exiting. A killed worker can lose changes made since the last sync interval. - Changes are not instantly visible across sessions. With the default 15-second interval, another session reading the same store typically sees updates within about a minute, not immediately.
- Not available on Claude Platform on AWS at all, cloud or self-hosted.
Sandbox filesystem layout
/workspace
├── skills/
│ └── <skill_name>/ # Downloaded agent skills
└── <agent output files> # Final deliverables
/mnt/memory/
└── <store_name>/ # Synced memory store directories
└── .anthropic-memory-store # Marker file, do not modify
One structural difference from cloud worth knowing if you are porting an agent over: there is no /mnt/session/outputs instruction in the system prompt on self-hosted sandboxes. Final deliverables land wherever the agent actually writes them in your filesystem, so your own tooling needs to know where to look rather than relying on a fixed output path.
Networking: self-hosting and MCP tunnels are independent controls
It is easy to conflate these two, but they solve different problems. Self-hosting controls where code executes. MCP tunnels control how Anthropic reaches your MCP servers over a private network path, independent of where the agent's tools themselves run. You can combine them in any of three ways:
- Cloud sandbox with an MCP tunnel: execution stays with Anthropic, but private tools stay reachable without exposing them publicly.
- Self-hosted sandbox with a public MCP server: execution and tool access both stay simple, nothing private to route.
- Self-hosted sandbox plus an MCP tunnel: both execution and tool access sit inside your own network boundary.
Serving custom tools directly from your worker
A self-hosted worker can also serve its own tools, useful for reaching internal services an MCP server was never built for:
from anthropic import beta_async_tool
from anthropic.lib.environments import EnvironmentWorker
@beta_async_tool
async def get_order_status(order_id: str) -> str:
"""Reaches internal fulfillment system from your sandbox."""
return f"Order {order_id}: shipped"
worker = EnvironmentWorker(
client,
environment_id=environment_id,
environment_key=environment_key,
workdir="/workspace",
tools=lambda env: [*beta_agent_toolset_20260401(env), get_order_status],
)
Wrapping a private MCP server as a custom tool
If an MCP server sits on your internal network with no public endpoint, the worker can keep an open MCP session and expose its tools to the agent as custom tools, forwarding calls and posting results back:
from anthropic.lib.tools.mcp import async_mcp_tool
from mcp.client.streamable_http import streamable_http_client
MCP_SERVER_URL = "http://mcp.internal.example.com:8000/mcp"
async with streamable_http_client(MCP_SERVER_URL) as (read, write, _):
async with ClientSession(read, write) as mcp_session:
await mcp_session.initialize()
listed = await mcp_session.list_tools()
mcp_tools = [async_mcp_tool(tool, mcp_session) for tool in listed.tools]
await EnvironmentWorker(
client,
environment_id=environment_id,
environment_key=environment_key,
workdir="/workspace",
tools=lambda env: [*beta_agent_toolset_20260401(env), *mcp_tools],
).run()
What self-hosting does not fix
It is tempting to reach for a self-hosted sandbox as a compliance shortcut, and it genuinely helps with data residency, egress control and audit trails, since code execution never leaves your infrastructure. But it does not change Managed Agents' platform-level data handling. As covered in our wider look at Managed Agents, the product as a whole is not currently eligible for Zero Data Retention or a HIPAA Business Associate Agreement, regardless of environment type, because sessions are stateful by design and Anthropic's control plane still retains conversation history, session state and events. If ZDR or a BAA is a hard requirement, self-hosting the sandbox does not clear that bar on its own; the Messages API remains the route for that.
Choosing between them
Reach for a cloud environment when you want the least setup: no worker process to run, GitHub repository mounting and skill discovery available out of the box, and Anthropic managing the sandbox lifecycle end to end. Most agents that do not have a specific compliance or network-locality requirement belong here.
Reach for a self-hosted sandbox when tool execution genuinely needs to happen inside your own network boundary, against data or services that cannot be exposed to an external sandbox, and you are prepared to run and monitor a worker process, plan for SDK-only memory stores, and build a small metadata-based staging step in place of native file and repository mounting.
Troubleshooting
A session creation request returns a 400 naming the environment as self-hosted. You included a file or github_repository resource. Neither is supported on self-hosted environments; switch to the metadata-and-staging workaround, or move the workload to a cloud environment if repository mounting is a hard requirement.
Memory stores are not syncing on a self-hosted deployment. Confirm you are using the SDK's EnvironmentWorker, not ant beta:worker poll, which does not mount memory stores at all. Also confirm you are not on Claude Platform on AWS, where memory stores are unsupported regardless of worker type.
The worker loses in-progress memory changes on redeploy. Check your shutdown signal. A SIGKILL gives the worker no chance to run its final sync; use SIGTERM or SIGINT and give it time to complete before the process is forcibly removed.
Two workers keep fighting over the same memory store. Only one session can mount a given store on a given host at a time. If your deployment scales workers horizontally, route sessions that use the same store to the same host, or serialise access at your own queue layer.
Final outputs are not where I expected them. Self-hosted sandboxes do not get the /mnt/session/outputs instruction cloud sessions have in their system prompt. Check your agent's actual working directory writes instead of assuming a fixed output path.
Where to go next
For the full Managed Agents concept model, agents, environments, sessions and events, see Claude Managed Agents explained. For running several agents in one session regardless of environment type, see multiagent orchestration in Claude Managed Agents. For the comparable self-hosting decision on the Claude Code side rather than the API, see how to self-host Claude Code runners. The ant CLI commands referenced throughout are covered in full in Anthropic's ant CLI. Browse the wider catalogue at getclaudeskills.com/skills or by category.
Verified 27 August 2026 directly against Anthropic's self-hosted sandboxes documentation at platform.claude.com/docs/en/managed-agents/self-hosted-sandboxes, read in full.
