Run your first AI agent in a Docker Sandbox
- Beginner
- 15 min
- Updated
- Verified
What you'll learn
- Store an agent's credentials on your machine so the sandbox never holds them
- Start Claude Code in a sandbox on one of your own projects
- Find a running sandbox and run commands inside it from your host
- Show that your files and your real keys aren't inside the sandbox
- See a network request blocked by policy, allow it, and retry
- Stop and remove a sandbox
A Docker Sandbox runs an AI coding agent inside an isolated microVM. The agent works on your project, but it can’t see the rest of your machine, it reaches the network only through a proxy that enforces your network policy, and it never holds your real credentials.
In this guide, you’ll run Claude Code in a sandbox against one of your own projects and test each of those boundaries. You’ll watch the network proxy block a request, allow it, and then clean everything up.
Before you start
You can follow this whole guide in your browser. The terminals on this page are simulations: click through them to see what each command does.
To run it for real on your own machine, you need three things:
- The
sbxCLI, installed and signed in. Follow Install Docker Sandboxes, then runsbx login. - A way for Claude Code to reach its model. Either a Claude subscription or an Anthropic API key.
- A project to work on. Any directory works. This guide calls it
~/my-app. A Git repository is best, because you can review what the agent changed withgit diff.
Your output will differ from the simulations in the details: your paths, your project, and what the agent says.
Step 1: Give the agent its credentials
An agent can’t think without its AI model, and the model wants a key. The risky way to provide it is to paste the key into the agent’s environment, where anything the agent reads could talk it into handing the key over.
A sandbox never puts your real key inside. You store it on your machine, in your operating system’s keychain, and the sandbox’s network proxy attaches it to the agent’s requests on their way out.
How you do this depends on how you pay for Claude:
- With a Claude subscription, there’s nothing to store yet. When Claude
Code starts in Step 2, type
/loginand sign in. The sign-in token stays on your machine, not in the sandbox. - With an API key, store it now.
sbxprompts for the value, so it never lands in your shell history:
Host machine
sbx secret set anthropic
Saved secret anthropic (global)
The secret is global: it’s stored once, on your machine, for any sandbox
that asks for it. Run sbx secret ls any time to see what’s stored. It lists
names, never values.
Storing a key doesn’t hand it to anything yet. In the next step, the sandbox asks for it, and you decide.
Step 2: Start Claude Code on your project
Change into your project and start Claude Code:
$ cd ~/my-app
$ sbx run docker/sbx-kit-claude --name claude-my-appdocker/sbx-kit-claude is a kit: a published bundle with Claude Code, the
tools it works with, and the rules for where it may connect and which keys it
may use. sbx pulls it from Docker Hub. --name gives the sandbox a name
you’ll use for the rest of this guide.
The very first time you run a sandbox, sbx asks which network policy to start
from:
Initialize the global network policy for your sandboxes:
Applies to all sandboxes, current and future — change it later with
"sbx policy allow/deny/rm". Kits, including built-in agent kits, may
also add per-sandbox rules.
1. Open — All network traffic allowed, no restrictions.
❯ 2. Balanced — Default deny, with common dev sites allowed.
3. Locked Down — All network traffic blocked unless you allow it.
Use ↑/↓ or 1–3 to navigate, Enter to confirm, Esc to cancel.Choose Balanced. It lets the agent reach AI providers, package managers, code hosts, and container registries, and blocks everything else. Step 5 depends on it.
Next, sbx asks you to approve the kit’s use of a credential. The kit says
it needs the anthropic service, and names the Anthropic domains the network
proxy may attach your key to. This is sbx checking with you, not the agent
asking: storing a key in Step 1 didn’t let any kit use it, and approving here
lets this kit’s sandboxes use it only on those domains. Read the domains, then
choose Approve all. sbx records your answer, so you aren’t asked again
unless a kit wants the key on more domains.
If you decline, the sandbox still starts, but the proxy won’t attach a key, so Claude Code can’t reach its model.
The first run also takes a minute while sbx pulls the kit. After that,
sandboxes start in seconds. Then you’re talking to Claude Code, running inside
the sandbox. Give it something to do:
Terminal 1 · Claude Code in the sandbox
sbx run docker/sbx-kit-claude --name claude-my-app
This kit wants to use these credentials:
anthropic: API key sent to api.anthropic.com, mcp-proxy.anthropic.com (stored)
OAuth refreshed at platform.claude.com
Approve these credentials?
1. Approve all
2. Review each
3. No start without them
Choice [1-3, default 3]: 1
✓ Binding saved to /home/moby/.config/sbx/credentials.yaml
SANDBOX
Name: claude-my-app
Agent: sbx-kit-claude
Kit: docker/sbx-kit-claude
Workspace: ~/my-app (rw)
Skills: 0 entries · read-only
Source: ~/.local/state/sandboxes/sandboxes/agent-skills
Target: /home/agent/.claude/skills
Image: docker.io/docker/sbx-kit-claude@sha256:c0132e89a89d94ddca3984513afdb36be57bd087fcc75823a4d14064b6a39ea5
CPU: 4
Memory: 4g
IMAGE
✓ image — ready
✓ Created sandbox claude-my-app
○ sbx-kit-claude — starting in sandbox claude-my-app…
workspace /home/moby/my-appWhat's in this project? Answer in one sentence.
A small Node.js web app: package.json, a src/ folder with an Express server, and a README.
Your answer depends on your project, but notice that Claude didn’t ask permission before reading your files. Inside a sandbox,
Claude Code starts in its bypass-permissions mode
(--dangerously-skip-permissions), so it doesn’t stop to ask before every
command. That’s safe to do here because the sandbox, not a stream of approval
prompts, is what keeps it contained.
Leave this session open. Use a second terminal for the rest of the guide.
Step 3: See the running sandbox
In a second terminal on your host, list your sandboxes:
Terminal 2 · Host machine
sbx ls
SANDBOX AGENT STATUS PORTS WORKSPACE CREATED claude-my-app sbx-kit-claude ● running /home/moby/my-app 3m
The WORKSPACE column is the one directory on your machine that the sandbox can see. It’s shared, not copied: the agent’s edits land in your project as it makes them.
Run sbx on its own to open a live dashboard of every sandbox, with their
CPU and memory use and a panel for network rules.
Step 4: Test the isolation
sbx exec runs a command inside the sandbox from your
host, so you can look around from the agent’s side.
Your other files aren’t there
Start with something that’s definitely on your machine but outside the
project, like your Documents folder. Your shell expands ~ before the
command reaches the sandbox, so this asks the sandbox for your real home
directory’s path:
Terminal 2 · Host machine
sbx exec claude-my-app ls ~/Documents
ls: cannot access '/home/moby/Documents': No such file or directory
sbx exec claude-my-app ls ..
my-app
Your Documents folder isn’t blocked. It doesn’t exist in there. And the
directory around your project holds exactly one thing: the project. Sibling
projects, SSH keys, cloud credentials, photos, none of it was shared, so none
of it is in the sandbox.
The same goes the other way. Packages the agent installs, Docker images it builds, and files it writes outside the project all stay inside the sandbox, and disappear when you remove it.
The agent only holds a placeholder
Now look for the Anthropic key. Claude Code needs to find something that looks like a key before it will start, so the kit gives it one. If you stored an API key in Step 1, the kit tells Claude Code to get its key from a command, and that command just prints a placeholder:
Terminal 2 · Host machine
sbx exec claude-my-app grep apiKeyHelper /home/agent/.claude/settings.json
"apiKeyHelper": "echo proxy-managed",
That’s the key Claude Code holds: the words proxy-managed. When Claude Code
sends a request to Anthropic, the network proxy
replaces it with your real key on the way out. If you signed in with a
subscription instead, the sign-in file inside the sandbox holds placeholder
tokens in the same way. Either way, if the agent were tricked into leaking its
key, nobody could use it.
Step 5: See the network policy at work
The Balanced policy allows the places agents commonly need and nothing else.
Ask the sandbox to fetch a page from a site that no rule allows,
example.com:
Terminal 2 · Host machine
sbx exec claude-my-app curl -sS https://example.com
Approval required for example.com:443. Review and respond with: sbx policy approval ls
sbx policy approval ls
APPROVAL SANDBOX TITLE DETAIL OPTIONS network:IDBxSfVGPCuAPGkrgIwGCUQIfzf54kIz6lofLskPg70 claude-my-app deb.debian.org:80 Protocol: TCP Resource type: domain no matching allow rule (default deny) allow (Allow), dismiss (Dismiss) network:G5rVts0tU9heuVP2toTrcCAOLXROcYF_WavKzr2hzv8 claude-my-app http-intake.logs.us5.datadoghq.com:443 Protocol: TCP Resource type: domain no matching allow rule (default deny) allow (Allow), dismiss (Dismiss) network:xZCRq6wR9aj-dJwGTv2gxY1AafnNPMa_QLxYsiFrEHs claude-my-app example.com:443 Protocol: TCP Resource type: domain no matching allow rule (default deny) allow (Allow), dismiss (Dismiss)
sbx policy approval respond network:xZCRq6wR9aj-dJwGTv2gxY1AafnNPMa_QLxYsiFrEHs --option allow
Recorded answer Allow
sbx exec claude-my-app curl -sS https://example.com
<!doctype html><html lang=en><head><meta charset=utf-8><link rel=icon href=data:,><meta name=viewport content="width=device-width,initial-scale=1"><title>Example Domain</title><style>html{color-scheme:light dark;background:light-dark(#eee,#222)}body{font:16px/1.6 system-ui,sans-serif;max-width:26em;margin:auto;padding:25vh 2em 2em;text-align:center}</style></head><body><p>This domain is for use in documentation examples without needing permission. This is not a service; avoid relying on it for testing and monitoring purposes.</p><script src=/s.js></script></body></html>Here’s what happened:
- The proxy blocked the request. No rule matched
example.com, so the proxy blocked it and told the sandbox why. The site never heard from it. - The proxy asked you. A blocked destination becomes a pending approval on your machine. The agent can’t answer it. Only you can.
- You approved it, and the same command worked. The change applied to the running sandbox straight away, with no restart.
Approving the request allows one destination (example.com on port 443), for
this sandbox only. A different sandbox reaching for the same site would ask
again. Use your own approval ID; sbx policy approval ls prints it.
Your list may hold other rows too. Here, the sandbox had already reached for
deb.debian.org and Claude Code’s telemetry host since it started, and the
proxy blocked those the same way. Respond only to requests you recognize. Your
agent goes through the same proxy: when Claude reaches for a blocked site, the
approval waits for you here, and Claude picks up the access when it tries
again.
If you already know a sandbox needs a site, add the rule up front instead of waiting to be asked:
$ sbx policy allow network --sandbox claude-my-app example.comLeave off --sandbox to allow it for every sandbox. To see everything your
sandboxes have reached, and what the proxy blocked, run sbx policy log.
Step 6: Stop and remove the sandbox
Go back to Terminal 1 and type /exit to leave Claude Code. Leaving the
session doesn’t delete the sandbox. It keeps everything the agent installed,
so sbx run --name claude-my-app picks up where you left off.
When you’re finished with it, stop it and then remove it:
Terminal 2 · Host machine
sbx stop claude-my-app
Stopped sandbox claude-my-app
sbx ls
SANDBOX AGENT STATUS PORTS WORKSPACE CREATED claude-my-app sbx-kit-claude ○ stopped /home/moby/my-app 5m
sbx rm claude-my-app
Removed sandbox claude-my-app
sbx rm asks first, because removing a sandbox deletes the microVM and
everything in it, and can’t be undone. Your project is safe: it lives on your
machine, and only the sandbox’s view of it goes away. The secrets you stored
stay in your keychain for the next sandbox.
For a throwaway session, add --rm to sbx run. It removes the sandbox for you
when the agent exits.
What’s next
You’ve run a real agent on a real project and tested each boundary: your other files weren’t in the sandbox, the agent only held a placeholder key, and the network proxy blocked a request until you allowed it.
So far the sandbox has run the stock Claude Code kit. Next, Customize your sandbox with kits shows how to add your own tools or swap in a different agent, and after that you’ll capture the whole setup in a file you can share.
Part of a learning path
This works on its own and is also included in the paths below. Open a path to see the complete route.
Learning path
Get Started with Docker Sandboxes
Step 2 of 5
- Step 1: What is a sandbox?
- Step 2: Run your first AI agent in a Docker Sandbox (this page)
- Step 3: Customize your sandbox with kits
- Step 4: Define and share your sandbox environment
- Step 5: Integrating sandboxes into native apps
Table of contents