Self-hosted sandbox for Claude Managed Agents
Anthropic runs the agent. Each session's tools run on a VM of its own.
Shelley handles the setup. About 5 minutes.
Prefer a terminal?
curl -fsSL https://exe-staging.dev/deploy/claude-managed-agents/prompt.txt | ssh exe.dev new --prompt=/dev/stdinClaude Managed Agents runs Claude's agent loop on Anthropic's servers. In a self-hosted environment, the agent's tools run on machines you choose, which claim each session from the environment's queue.
This page makes each session's machine an exe.dev VM: a whole Ubuntu machine with sudo and Docker, ready in a few seconds and private by default. dispatcher.py, a short Python program, runs on a VM of its own (or on your computer) and, for each session it claims:
- finds the VM whose comment is the session's ID, or creates one, with exe.dev's Python SDK;
- installs Anthropic's
antCLI there, and startsant beta:worker runwith that session's own secret, as a systemd unit; - moves on to the next session. The worker runs the session's tools in
/workspaceand exits a minute after the session goes idle. A reply starts a new worker on the same VM, with the same files and packages.
What you get#
- The claude-dispatcher service, which gives each session in the environment a VM of its own
- The service restarted on failure and after a reboot
- The environment key and exe.dev API key in /etc/claude-dispatcher.env, mode 600
exe.dev key worksandenvironment key worksinjournalctl -u claude-dispatcher
What's running, and where
Deploy with Shelley#
Click Deploy and Shelley will set up Claude Managed Agents on a new VM, then check that it works. No config-file wrangling required.
Have these ready; Shelley asks for them:
ANTHROPIC_ENVIRONMENT_ID: your self-hosted environment's ID,env_...ANTHROPIC_ENVIRONMENT_KEY: its key,sk-ant-oat01-...EXE_TOKEN: the exe.dev API key thatssh exe.dev ssh-key generate-api-keyprinted,exe1....
Prefer a terminal?
curl -fsSL \
https://exe-staging.dev/deploy/claude-managed-agents/prompt.txt \
| ssh exe.dev new --prompt=/dev/stdinWhat Shelley is told
ANTHROPIC_ENVIRONMENT_ID=<fill-this-in>
ANTHROPIC_ENVIRONMENT_KEY=<fill-this-in>
EXE_TOKEN=<fill-this-in>
Set up Claude Managed Agents on this VM by following the exe.dev recipe at https://exe-staging.dev/deploy/claude-managed-agents.md from top to bottom.
The values at the top of this message are mine:
- ANTHROPIC_ENVIRONMENT_ID: your self-hosted environment's ID, `env_...`
- ANTHROPIC_ENVIRONMENT_KEY: its key, `sk-ant-oat01-...`
- EXE_TOKEN: the exe.dev API key that `ssh exe.dev ssh-key generate-api-key` printed, `exe1....`
Ask me for any that still say <fill-this-in> before you need them.
The recipe's example VM name is my-claude-dispatcher. This VM's name is the output of `hostname`: use it wherever the recipe writes my-claude-dispatcher. Skip the step that creates the VM; you are on it.
You are done when all of these are true. Check each one yourself:
- The claude-dispatcher service, which gives each session in the environment a VM of its own
- The service restarted on failure and after a reboot
- The environment key and exe.dev API key in /etc/claude-dispatcher.env, mode 600
- `exe.dev key works` and `environment key works` in `journalctl -u claude-dispatcher`
Run "Set up the dispatcher by hand" from "On the VM", skipping VM
creation and SSH. The three inputs are secrets: put them only in
/etc/claude-dispatcher.env, and never print them. Don't start a session
or run the owner's commands: they need the owner's own keys. If the log
shows `401` or `invalid token`, tell the owner which key was refused.
Finish by pointing the owner to "Give it a task".
Finish with the URLs I should open.
Also at /deploy/claude-managed-agents/prompt.txt. The recipe it follows is /deploy/claude-managed-agents.md.
Before you start#
-
A self-hosted environment and its key. In the Claude Console, click Create environment, choose Self-hosted under Hosting, then open it and click Generate environment key. Note its ID (
env_...) too. -
An exe.dev API key that can make and reach session VMs, and nothing else. On your computer:
ssh exe.dev ssh-key generate-api-key --label=claude-dispatcher \ --tag=claude-session --cmds=new,ls,ssh --exp=1y --json | jq -r .tokenIt sees only VMs tagged
claude-session, and the VMs it creates get that tag.ssh exe.dev ssh-key remove claude-dispatcherrevokes it.
Run the dispatcher#
Deploy sets it up on a new VM. To do it yourself:
Set up the dispatcher by hand
On your computer:
ssh exe.dev new --name=my-claude-dispatcher
ssh my-claude-dispatcher.exe.xyz
On the VM, download the dispatcher and write its settings, filling in your values:
sudo curl -fsSL -o /usr/local/bin/claude-dispatcher \
https://exe-staging.dev/deploy/claude-managed-agents/dispatcher.py
sudo chmod 755 /usr/local/bin/claude-dispatcher
sudo install -m 600 /dev/null /etc/claude-dispatcher.env
sudo tee /etc/claude-dispatcher.env > /dev/null <<'EOF'
ANTHROPIC_ENVIRONMENT_ID=env_...
ANTHROPIC_ENVIRONMENT_KEY=sk-ant-oat01-...
EXE_TOKEN=exe1....
EOF
Run it as a service:
sudo tee /etc/systemd/system/claude-dispatcher.service > /dev/null <<'EOF'
[Unit]
Description=Claude Managed Agents dispatcher (one exe.dev VM per session)
Wants=network-online.target
After=network-online.target
[Service]
User=exedev
EnvironmentFile=/etc/claude-dispatcher.env
ExecStart=/usr/local/bin/uv run --script /usr/local/bin/claude-dispatcher
Restart=always
RestartSec=10
[Install]
WantedBy=multi-user.target
EOF
sudo systemctl daemon-reload
sudo systemctl enable --now claude-dispatcher
sleep 10; journalctl -u claude-dispatcher -n 5 --no-pager
Expect exe.dev key works and environment key works. To update the
dispatcher later, run the curl line again and
sudo systemctl restart claude-dispatcher.
Or run it on your computer#
With uv, fill in the same three values and leave it running in a terminal:
export ANTHROPIC_ENVIRONMENT_ID=env_...
export ANTHROPIC_ENVIRONMENT_KEY=sk-ant-oat01-...
export EXE_TOKEN=exe1....
uv run https://exe-staging.dev/deploy/claude-managed-agents/dispatcher.py
Sessions wait in the queue while it isn't running. Run one dispatcher per environment.
Give it a task#
Start sessions as you always do, with the self-hosted environment's ID.
For example, with an agent (the
quickstart
makes one), the ant CLI, and ANTHROPIC_API_KEY set to your Claude API
key, on your computer:
ant beta:sessions create --agent agent_... --environment-id env_... \
--transform id -r --initial-event '
type: user.message
content: [{type: text, text: "Write the output of hostname to hello.txt."}]'
Within seconds the dispatcher logs sesn_...: worker started on <vm>.
Once the session goes idle, ssh <vm>.exe.xyz cat /workspace/hello.txt
prints that VM's name.
Clean up#
On your computer, ssh exe.dev ls lists each session's VM with the
session's ID as its comment. Delete one with ssh exe.dev rm <vm>; a
session that comes back after its VM is gone gets a new, empty one. To
delete every VM tagged claude-session (its first two lines alone list
them):
ssh exe.dev ls --json |
jq -r '.vms[] | select(.tags // [] | index("claude-session")) | .vm_name' |
xargs -r ssh exe.dev rm
Troubleshooting#
The dispatcher's log is journalctl -u claude-dispatcher on its VM:
Error code: 401or404: the environment key or ID is wrong.invalid token: the exe.dev API key is wrong or has expired; make a new one.during new: ...andgiving up: exe.dev refused a VM, often because the plan's VM limit is reached. Delete old session VMs.
A session's own log is on its VM:
ssh <vm>.exe.xyz journalctl -t claude-worker.
Frequently asked questions#
What runs where?
Anthropic runs Claude, the agent loop, sessions, and memory stores. Each session's VM runs its tools (bash, read, write, edit, glob, and grep) as exedev, with sudo, in /workspace. The dispatcher only claims work and starts workers. Tool inputs and outputs still pass through Anthropic; see its security model.
Which keys reach the session VMs?
Normally just each work item's secret, which authorizes that one session. A work item that comes without one gets the environment key instead, as in Anthropic's own example, and the dispatcher logs a warning. The exe.dev API key stays with the dispatcher, and your Claude API key with you.
What does it cost?
Anthropic bills sessions and model usage to your Claude account. Session VMs share your exe.dev plan's CPU, memory, and disk rather than being billed one by one, but each counts toward the plan's VM limit (50 on Personal). Delete them as sessions finish.
Can Claude show me a web app it built?
Yes. Anything it serves on 0.0.0.0, on a port from 3000 to 9999, is at https://<vm>.exe.xyz:<port>/, behind exe.dev login. The session's worker stops a minute after each turn, and everything it started stops with it, even with nohup or setsid. So say in the agent's system prompt to start servers with sudo systemd-run --uid=exedev --working-directory=$PWD <command>, and to run hostname for the VM's name.
Can Claude work on my GitHub repositories?
Not through a session's github_repository resource, which self-hosted environments reject. Attach exe.dev's GitHub integration to every session VM by their tag instead, on your computer: ssh exe.dev integrations add github --name myapp --repository you/myapp --attach tag:claude-session. Claude then clones https://github.int.exe.xyz/you/myapp.git, and no GitHub token reaches a VM.
Can sessions get bigger VMs, or another image?
Bigger, yes: add EXE_NEW_FLAGS=--cpu=4 --memory=16GB (or --disk=50GB) to /etc/claude-dispatcher.env and run sudo systemctl restart claude-dispatcher. New session VMs get them. Another image works if, like exe.dev's default, it is Debian-based with systemd and a sudo user exedev.
Can all sessions share one VM instead?
Yes, and then you don't need the dispatcher: on that VM, install ant and run ant beta:worker poll, which runs every session there in turn, in the same /workspace. Anthropic's worker guide covers it.
Updated Oct 23, 2026 · Markdown · manifest.json · Something wrong? Tell us