Get started with self-hosted Claude Managed Agents
Start self-hosted Claude Managed Agents on one machine with Docker Compose and run a first session in a sandbox container.
In this quickstart, you start self-hosted Claude Managed Agents on one machine with Docker Compose. The Compose stack is the server, its PostgreSQL database, and a set of workers that run each session in a sandbox container of its own. You then chat with an agent from your terminal and confirm that its tool calls ran in a sandbox container. You can then open the session in the console on your server.
For production, Anthropic recommends the release’s two Helm charts. See Deploy on Kubernetes.
Claude Code
Let Claude Code run this quickstart for you
Set up self-hosted Claude Managed Agents on this machine by following https://platform.claude.com/docs/en/managed-agents/self-hosted/quickstart.md. I've already downloaded the release archive.Prerequisites
- The release archive: To request access to the download, contact your Anthropic account team.
- A Claude API key: The server uses it for model calls. Create one in the Claude Console.
- Docker: The latest version of Docker Engine with the Compose plugin (
docker compose, not the olderdocker-compose). - A host with at least two CPUs: x86-64 or Arm. On a smaller host, set
CMA_SANDBOX_CPUSto its CPU count when you create.env. - Network access on first start: The first build pulls from Docker Hub, the Alpine package repositories, and GitHub releases.
- Tools: On the machine where you run Docker, you need
openssland theantCLI.
Start the stack
Extract the archive
Extract the archive, and work from its
composedirectory. In the command, replace<archive>with your archive’s name, without.tar.gz.tar -xzf <archive>.tar.gz && cd <archive>/composeIf your umask is stricter than
022, the binaries lose their execute permission for other users, and the server container can’t start them. In that case, set it to022and extract the archive again.Create the .env file
Compose reads the stack’s secrets from a
.envfile besidedocker-compose.yml. Create the file:cat > .env <<EOF CMA_PG_PASSWORD=$(openssl rand -hex 32) ANTHROPIC_API_KEY=sk-ant-... CMA_SERVER__API_KEY=$(openssl rand -hex 32) CMA_STORE__CREDENTIALS_ENCRYPTION_KEY=$(openssl rand -base64 32) EOF chmod 600 .envOpen
.envin an editor and replacesk-ant-...with your Claude API key. The block generates the rest: the database password, the server API key that your applications send, and the key that encrypts the server’s stored secrets.Start the containers
Build and start the stack, and then follow the server’s log:
docker compose up --build -d docker compose logs -f cma-serverWhen the log shows a line with
msg="cma-server listening", press Ctrl+C to stop following it.Check that the server is ready
curl -s http://127.0.0.1:8400/cma-server/readyzA ready server answers with
"status":"ready". Right after start, it can answer"status":"degraded"for a few seconds while its first checks run. If it staysdegradedor answers"status":"not_ready", see Troubleshooting.
Run your first session
Create an agent and chat with it from your terminal with the ant CLI.
Point the CLI at your server
Open a second terminal in the same directory, and keep it for
ant. The following commands setANTHROPIC_API_KEYto your server API key, not your Claude API key, as Call the API on a self-hosted server explains:export ANTHROPIC_BASE_URL=http://127.0.0.1:8400 export ANTHROPIC_API_KEY=$(grep '^CMA_SERVER__API_KEY=' .env | cut -d= -f2)Keep
docker composecommands in the first terminal. If you rundocker compose upin this terminal, Compose passes these variables to the server, and the server calls itself instead of the Claude API.Create an agent
Save the file that follows the command as
quickstart-agent.md, and then run the command. The file gives the agent the built-in tools and lets it run them without asking you first.CLIant apply --yes quickstart-agent.mdquickstart-agent.md--- name: Quickstart agent model: claude-opus-5-5 tools: - type: agent_toolset_20260401 --- You are a helpful assistant.The command first prints a warning that it
could not determine the organization and workspace, with a404error, and then creates the agent. That’s expected on your own server.Start a session
These commands find the agent and the stack’s environment, start a session, and keep the session’s ID in
SESSION_ID. The stack names its environmentcompose-sandbox.AGENT_ID=$(ant beta:agents list --format raw \ --transform 'data.#(name=="Quickstart agent").id' --raw-output) ENVIRONMENT_ID=$(ant beta:environments list --format raw \ --transform 'data.#(name=="compose-sandbox").id' --raw-output) SESSION_ID=$(ant beta:sessions create --agent "$AGENT_ID" \ --environment-id "$ENVIRONMENT_ID" --transform id --raw-output)Ask the agent to run a command
Connect your terminal to the session:
ant beta:sessions connect "$SESSION_ID"The command opens a chat view of the session. Send this message:
Run `hostname` with the bash tool and tell me the output.The agent replies with a hostname that starts with
cma-sandbox-. That’s the name of the sandbox container that ran the command, which confirms that the tool ran in a sandbox and not on your machine.Find the sandbox container
Leave the chat open. In the first terminal, list the sandbox containers:
docker ps --filter label=cma-server.sandboxThe list shows the container that the agent reported. The container exits about a minute after a turn ends, and the session’s next message starts a new container. If the list is empty, ask the agent to run
hostnameagain, and list the containers again while it replies.To leave the chat, press Ctrl+C in its terminal. The session keeps running.
Try the console on your server
This section is optional. The console needs single sign-on. To try it, restart the stack with the release’s mock sign-in provider. Port 8080 on your machine must be free.
Restart the stack with the mock sign-in provider
Run these commands in the first terminal:
export COMPOSE_FILE=docker-compose.yml:docker-compose.oidc.yml docker compose up --build -dThe
exportline holds for the first terminal only, so run it again in any new terminal that you open fordocker compose. To change the server’s settings in this stack, editcompose.config.oidc.yaml, notcompose.config.yaml, and then rundocker compose restart cma-server.Open the session
In a browser on the same machine, open
http://127.0.0.1:8400/, click Continue with SSO, and select alice, the demo administrator. In the sidebar, select Sessions, and then select the session.The page updates as the agent works, and you can send the agent a message from it.
To create agents and start sessions in the console too, see Run an agent from the console.
Stop the stack
In the first terminal, run this command:
docker compose down --remove-orphansThe command deletes the containers and keeps the database. To also delete the database, with every agent, session, and session history in it, run docker compose down --remove-orphans -v.
Troubleshooting
- Your archive has no
composedirectory: The archive holds an earlier release, and this page’s commands don’t match it. Ask your Anthropic account team for the current release. To move a running deployment to the current release, see Upgrade self-hosted Claude Managed Agents. - The server never comes up: Run
docker compose logs migrate. The one-timemigratecontainer creates the database tables. It must exit with status0before the server starts. - The readiness check stays
degraded, or every turn fails: If you randocker compose upin a terminal whereANTHROPIC_BASE_URLishttp://127.0.0.1:8400, the server calls itself instead of the Claude API. Rundocker compose up -dagain in the first terminal, where the variable isn’t set. Otherwise, the server log shows what failed. To read it, rundocker compose logs -f cma-serveragain. - The readiness check answers
not_ready: In the answer, find the check with the statefailed:upstream: The Claude API rejected your Claude API key. CorrectANTHROPIC_API_KEYin.env, then rundocker compose up -dagain in the first terminal. If your shell also setsANTHROPIC_API_KEY, Compose uses the shell’s value, so rununset ANTHROPIC_API_KEYfirst.database: Either the database rejected the server’s login, for example because of a wrong password, or an open connection to the database broke. To see which, rundocker compose logs -f cma-serveragain.
- The agent replies but its tool call never finishes: No worker is polling the environment. Check the workers’ log with
docker compose logs sandbox. A worker that can’t reach the Docker daemon’s socket exits with a message that namesCMA_DOCKER_SOCKET. Set that variable in.envto your daemon’s socket path, then rundocker compose up -dagain in the first terminal. - The workers are running, but a tool call still never finishes: The stack starts 10 workers, and each one runs one session’s container at a time. A session takes a free worker when you create it, and a session that never gets a message keeps its worker for up to an hour. With all 10 taken, any other session’s tool calls wait for a worker. If the
docker pscommand in the “Find the sandbox container” step lists 10 containers, archive the sessions that you don’t use:- In the first terminal, run
docker ps --filter label=cma-server.sandbox --format '{{.Label "cma-server.session"}}'. It prints the ID of the session behind each container. The session that waits has no container yet, so it isn’t in the list. - In the second terminal, press Ctrl+C to leave the chat, and run
ant beta:sessions archive --session-id <session-id>for each listed session that you don’t use. - Run
ant beta:sessions connect "$SESSION_ID"again. The tool call finishes when a worker takes its session.
- In the first terminal, run
Next steps
Point the SDK and the ant CLI at your server.
Check which features behave differently or aren’t available.
Set up sign-in, and create agents and run sessions in a browser.
Was this page helpful?