Run and operate Docker Sandboxes — choosing an agent, passing it flags, git workspace modes, ports, secrets, network policy, and sharing host skills into a sandbox. Use when starting or re-attaching to a sandbox, picking between the built-in agents, signing in from CI, copying files in or out, or reaching for the right sbx command.
74
92%
Does it follow best practices?
Run evals on this skill
Adds up to 20 points to the overall score
View guide
Passed
No findings from the security scan
Docker Sandboxes (sbx) runs AI coding agents inside isolated microVMs. Each sandbox gets
its own Linux kernel, its own Docker Engine, its own network, and a host-side proxy that
enforces network policy and injects credentials so secrets never enter the VM.
This is the operating surface — running sandboxes, choosing and configuring the built-in agents, controlling egress, and wiring credentials. Seven sibling skills carry the rest:
creating-sbx-templates — the answer is a Dockerfile: base variants, extending one,
families built with bake, publishing.creating-sbx-kits — the answer is a spec.yaml: setup steps, permissions,
credentials, kind: mixin vs sandbox, v1→v2 migration.diagnosing-sbx-sandboxes — something is blocked, killed, or gone.governing-sbx-fleets — the administrator who writes the policy, rather than the
developer it blocked: org vs local rules, audit delivery, sign-in enforcement.wiring-sbx-mcp-servers — sbx mcp registration, the gateway, OAuth, Cedar policy.connecting-to-sbx-sandboxes — driving a sandbox from VS Code, Cursor, Claude
Desktop or ChatGPT over SSH.finding-sbx-docs — confirming a claim against the official docs, and refreshing
the local mirror of them.Synthesized from the official documentation at docs.docker.com/ai/sandboxes/. Docker
serves .md for every docs URL, so append .md rather than scraping when checking a
claim; finding-sbx-docs carries that loop. Where the docs disagree with themselves,
see Known Discrepancies.
Choosing and configuring an agent — picking which built-in agent to run or how it
authenticates; an agent that rejects its credential or loses its login on recreate; flags
and prompts passed through sbx run <agent> --; missing user-level config (~/.claude,
~/.cursor, ~/.gemini) inside the sandbox.
Operating sandboxes — running, naming, re-attaching to, removing sandboxes; mounting extra
workspaces; choosing direct mode vs clone mode (--clone) vs a host worktree, especially
with several agents on one repository; publishing ports or reaching a host service from inside;
CI / headless use; shared agent skills.
Policy and credentials — a download, git clone, apt-get or API call is blocked and
you need the right sbx policy allow rule; storing an API key, GitHub token, registry
credential or custom-format secret; a local allow rule that has no effect (org
governance); Cedar MCP policies.
When the sandbox will not start, a kit will not load, or something written inside
vanished, that is diagnosing-sbx-sandboxes. When the answer is a spec.yaml or a
Dockerfile, it is creating-sbx-kits or creating-sbx-templates.
Do not reach for this for generic Docker/Compose questions — sandboxes have their own daemon and their own rules, and generic Docker advice usually misleads here.
| Term | What it means |
|---|---|
| Sandbox | A microVM with its own kernel, Docker Engine, filesystem and network. The agent has full root/sudo inside it. The VM boundary — not in-VM privilege separation — is the isolation control. |
| Template | A Docker image the sandbox runs, published as docker/sandbox-templates:<variant>. Built ahead of time, cached locally. Use for heavy things: system packages, toolchains, large deps. |
| Kit | A YAML artifact (spec.yaml) applied at sandbox creation. Runs install/startup commands, injects files, declares network + credential rules. Use for thin, per-team, per-project things. |
| Mixin kit | kind: mixin — layers capability onto an existing agent. Must not declare sandbox:, extends:, or mixins:. |
| Sandbox kit | kind: sandbox — defines a whole agent (image + entrypoint). The built-in claude agent is itself a kit. |
| Direct mode (default) | Host working tree is bind-mounted at the same absolute path. Agent edits are live on your host instantly. |
Clone mode (--clone) | The agent works in a private in-VM Git clone. Your host repo is mounted read-only at /run/sandbox/source. Fetch work back via the sandbox-<name> Git remote. Create-time only. |
| Forward proxy | Host-side HTTP/HTTPS proxy. Enforces policy and injects credentials. |
| Transparent proxy | Fallback path for clients not configured to use the forward proxy (e.g. a process inside a nested Docker container). Enforces policy but does not inject credentials. |
| Sentinel / proxy-managed | The fake value the agent sees in place of a real credential. The proxy swaps in the real one on the way out. |
| Credential binding | ~/.config/sbx/credentials.yaml — a record of which credential mechanisms and domains you approved for a third-party schemaVersion: "2" kit. |
/etc/sandbox-persistent.sh | The sandbox's persistent env file, sourced before every bash invocation. Where custom env vars and version-manager init lines go. |
Hypervisor → network → Docker Engine → workspace → credential proxy. Two consequences trip people up constantly:
sbx login # first thing on a new machine
sbx run claude # start an agent (workspace = cwd)
sbx run claude --name my-sandbox ~/my-project # explicit identity
sbx create ~/my-project --name bg-box # create without attaching (path required)
sbx exec my-sandbox # get a shell inside
sbx ls # what's running, with ports + workspaces
sbx stop my-sandbox # pause — VM state is preserved
sbx rm my-sandbox # delete it entirelysbx login opens a browser. Where there is no browser — CI, a remote runner —
authenticate with a Docker personal access token of at least Read scope:
echo "$DOCKER_PAT" | sbx login --username <docker-id> --password-stdinMove one-off files in and out with sbx cp. Exactly one side carries
SANDBOX:PATH, and sandbox-to-sandbox is unsupported:
sbx cp ./config.json my-sandbox:/home/agent/
sbx cp my-sandbox:/home/agent/output.log ./Anything copied to a path that is not a mounted workspace lands on microVM root
disk and dies with the sandbox — see diagnosing-sbx-sandboxes.
sbx run against the same workspace path re-attaches rather than creating a second sandbox,
so give each one a distinct --name to run several against one workspace. sbx rm refuses while
a session is attached unless you pass --force. Run sbx with no subcommand for the interactive
dashboard (live status + network governance panel; tab switches panels, ? for keys).
sbx run claude --name my-sandbox -- "Add error handling to the login function"
sbx run shell -- -c "echo 'Hello from sandbox'"Everything after -- goes to the agent, and the merge rule is the most misunderstood thing in
the CLI. If the first argument after -- starts with - it is appended to the default
flags, so --dangerously-skip-permissions, --yolo or bash -l survive; a bare word (a
subcommand or a prompt) replaces the defaults entirely.
That is why sbx run kiro -- login --use-device-flow runs the login subcommand alone, and
why sbx run docker-agent -- run --yolo must spell out run. To get a subcommand with
the usual flags, write both: -- chat --trust-all-tools --resume.
| Agent | Authentication | Template (docker/sandbox-templates:) |
|---|---|---|
claude | sbx secret set anthropic, or /login in-session for a Claude subscription (OAuth) | claude-code |
codex | Host-side OAuth (prompted before launch if nothing is stored), or sbx secret set openai | codex |
cursor | Stored API key, or OAuth — the proxy intercepts the exchange at api2.cursor.sh/auth/poll | cursor-agent-docker |
gemini | Google API key, or interactive Google sign-in scoped to that sandbox | gemini |
copilot | A GitHub token with Copilot access, via stored secrets | copilot |
kiro | Device flow in a browser; state in ~/.local/share/kiro-cli/data.sqlite3 | kiro |
droid | Factory account — API key or OAuth. Factory brokers model access, so no provider key | droid-docker |
opencode | Multi-provider, chosen in the TUI. Zen keys need set-custom + opencode.ai allowed | opencode |
docker-agent | Multi-provider: openai, anthropic, google, xai, nebius, mistral, openrouter | docker-agent |
shell | None — no agent binary; runs bash -l. Store secrets first; the proxy still injects | shell |
Interactive logins die with the sandbox. Gemini's Google sign-in and Kiro's device
flow survive a restart but not sbx rm + recreate; a stored secret survives both.
cursor, droid and codex keep OAuth on the host, so no token lands in the VM.
No user-level host config is visible inside. ~/.claude, ~/.cursor, ~/.gemini
are not mounted; only project-level config in the workspace applies. Ship user-level
settings through a kit setup.startup step or a template.
Several agents ship pre-loosened: kiro defaults to chat --trust-all-tools,
copilot trusts the workspace, droid runs without approval prompts, and gemini has
its own sandboxing disabled (the microVM already provides it). The reasoning is that the
microVM is the boundary, so the prompts guard nothing the isolation does not already
cover. Switch a single session back with the agent's own control — /permissions in
Claude Code. To make it the default, fork the agent in a sandbox kit and drop the
permission-skipping flag from the entrypoint:
# claude-safe/spec.yaml
schemaVersion: "2"
kind: sandbox
name: claude-safe
sandbox:
image: docker/sandbox-templates:claude-code-docker
entrypoint: [claude]sbx run claude-safe --kit ./claude-safe/The published FAQ still shows this example in v1 (schemaVersion: "1" with
entrypoint.run). Write it as above; see creating-sbx-kits for the grammar.
claude --model routes Anthropic API requests to a model served on your host — a GGUF
reference for the bundled llmman server, or ollama/<name> for an Ollama install you start
yourself. Experimental, unsupported on Windows, and it recreates the container (workspace and
kit-owned volumes persist).
Two artifacts. A kit (spec.yaml) is applied at creation and declares what a sandbox
may do; a template is an image and decides what is installed. Reach for a kit first —
--kit repeats, so a capability written once composes onto every agent.
sbx run claude --kit ./web-dev --kit ./shopify # stack them, order is install order
sbx kit validate ./web-dev # an unknown key is a hard error
sbx kit add my-sandbox ./web-dev # apply to a running box; restarts itWriting either one is a sibling skill: creating-sbx-kits for the spec, and
creating-sbx-templates for the image. Both carry worked, commented assets.
sbx policy log # what got blocked, and why
sbx policy allow network api.example.com
sbx policy deny network ads.example.com
sbx policy ls # active rules and their origin
sbx policy check network api.example.com # would this be allowed? (no sandbox needed)
sbx policy init balanced # allow-all | balanced | deny-allDeny always beats allow. sbx policy check evaluates a bare hostname against port 443;
--sandbox <name> scopes a rule or check to one sandbox, --wide adds rule IDs to
policy ls. In CI, set the preset with policy init before any other sbx command.
sbx policy allow network "10.1.2.3:22"Hostname rules such as myhost:22 do not work for non-HTTP connections — the proxy
cannot resolve the name in that context. UDP and ICMP cannot be unblocked at all. For Git
over SSH, allow the server's IP or switch the remote to HTTPS.
If the host runs an SSH agent with SSH_AUTH_SOCK set, sbx forwards it in and sets
SSH_AUTH_SOCK inside. Private keys stay on the host — in-VM processes can request
signatures but cannot read the key. That is what makes SSH commit signing work, and the
key must already be loaded in the host agent.
Signing takes two steps. Load the key on the host, then configure Git inside against the forwarded key rather than a file path, because host paths do not exist in the VM:
ssh-add ~/.ssh/id_ed25519 && ssh-add -L # host
git config --global gpg.format ssh # sandbox
git config --global user.signingkey "key::$(ssh-add -L | head -n 1)"
git commit -S -m "feat: my change"The git-ssh-sign community kit in docker/sbx-kits-contrib packages exactly this, so
every sandbox gets it without the manual step.
sbx secret set anthropic # prompts; global by default
sbx secret set github -t "$(gh auth token)" # gives the agent a working gh CLI
sbx secret set anthropic --sandbox my-box # sandbox-scoped: applies immediately
sbx secret lsA sandbox-scoped secret takes effect immediately, even on a running sandbox; a global one applies only at creation, so changing it means recreating. Stored secrets beat every other source. The real value never enters the VM — the agent holds a placeholder and the proxy swaps it in flight.
Custom-format secrets, registry credentials, where secrets are stored on each host OS, and the mechanics of injection are in references/credentials.md.
sbx run claude --publish 8080:3000 # host 8080 → sandbox 3000, at creation
sbx ports my-sandbox --publish 3000 # existing sandbox; OS picks the host port
sbx ports my-sandbox # see what was assigned
sbx policy allow network "localhost:3000" # before reaching a host serviceThe service must bind 0.0.0.0 or [::] inside — 127.0.0.1 is not reachable through the
mapping — and sbx run ignores --publish when re-attaching, so use sbx ports. The
other direction: reach a host service as host.docker.internal (not 127.0.0.1, not your
LAN IP), with the port allowed in policy first.
connection reset by peer on localhost while 127.0.0.1 works is the address-family
trap, not a broken mapping. --publish listens on both 127.0.0.1 and ::1, so a client
resolving localhost may pick IPv6 and hit a service listening on IPv4 only. Bind the
service to [::], or pin the family: --publish 8080:3000/tcp4.
Published ports survive restarts and are re-published automatically. An explicit host port is
reused; an OS-assigned one (--publish 3000) changes on every start, so read it back from
sbx ports. --unpublish 8080:3000 drops one mapping, --unpublish 3000 drops every host
port mapped to that sandbox port.
sbx exec -d my-sandbox bash -c "echo 'export BRAVE_API_KEY=key' >> /etc/sandbox-persistent.sh"The bash -c wrapper is required so >> runs inside the sandbox. This stores the value
in the VM where the agent can read it — prefer sbx secret whenever proxy injection is an
option. When an agent merely requires a variable to exist (OPENAI_API_KEY and friends in
a hand-built template), set it to proxy-managed; the proxy injects the real credential
regardless of the value it replaces. The same file makes a version manager available
everywhere — but append the init script only, never the completion script, since it is
sourced before every command and completion scripts break non-interactive shells.
sbx skills import --dry-run # what would be copied, copies nothing
sbx skills import # copy; prompts before replacing an existing skill
sbx skills import --force # replace without promptingImporting copies skill directories from the host into one persistent store, which every new
sandbox running a supported agent mounts at the path that agent reads. The store outlives
sbx rm because it is host state, and sandboxes mount its current contents at every start —
so import order relative to sandbox creation does not matter.
| Agent | Host source | Mounted at |
|---|---|---|
| Claude Code | ~/.claude/skills | /home/agent/.claude/skills |
| Codex | ~/.agents/skills | /home/agent/.agents/skills |
| Copilot | ~/.copilot/skills | /home/agent/.copilot/skills |
| Cursor | ~/.cursor/skills | /home/agent/.cursor/skills |
| Droid | ~/.factory/skills | /home/agent/.factory/skills |
Every source lands in the same store. On a directory-name collision the first source in
that table wins and the rest are warned about. Import replaces a whole skill directory rather
than merging files, so re-run it to pick up host edits. Directory symlinks under the skills
folder are resolved and their contents imported. sbx reset clears the store.
The store is one host directory, and sbx skills import prints its path when it finishes:
| Platform | Shared store |
|---|---|
| macOS | ~/Library/Application Support/com.docker.sandboxes/sandboxes/agent-skills |
| Linux | ${XDG_STATE_HOME:-~/.local/state}/sandboxes/sandboxes/agent-skills |
| Windows | %LOCALAPPDATA%\DockerSandboxes\sandboxes\state\agent-skills |
The store is mounted read-write, which puts every sandbox sharing it inside one trust
boundary: a skill rewritten by one sandbox is loaded, and its scripts run, by the next. That
is sandbox state rather than host state, so it does not by itself execute anything on your
machine. --no-share-skills at creation keeps a sandbox outside the boundary — and like
--kit and --clone it is create-time only, so changing your mind means a recreate.
Upgrading sbx does not retrofit the store onto sandboxes created before 0.37.0.
If an imported skill does not appear, start a fresh agent session — several agents only scan at startup.
sbx settings set clipboard.imagePaste trueText paste always works because the terminal sends it directly. An image is different: the
agent reads it from the host clipboard, which the sandbox blocks until you opt in. The
setting takes effect within seconds, on running sandboxes too. It is off by default because
it relaxes isolation — reads happen only on a paste, return image/png only, and are never
cached or logged, but it is still host data crossing in.
sbx diagnose # --output json | github-issue
sbx daemon restart # hangs / daemon errors — does not delete data
sbx reset --preserve-secrets # nuclear: stops all VMs, deletes all stateTry them in that order. sbx reset also clears the template cache, the shared skills store,
and — without --preserve-secrets — every stored secret.
The Quick Reference above is the 80% case. For anything deeper:
| Need | Go to |
|---|---|
Write or fix a spec.yaml | skill creating-sbx-kits, and its references/spec-fields.md for every field |
| Write a Dockerfile or a template family | skill creating-sbx-templates |
| Something blocked, killed, or vanished | skill diagnosing-sbx-sandboxes |
| Give an agent an MCP server, or govern its tool calls | skill wiring-sbx-mcp-servers |
| Attach VS Code, Cursor, Claude Desktop or ChatGPT over SSH | skill connecting-to-sbx-sandboxes |
| Administer org policy, filesystem rules, or audit logging | skill governing-sbx-fleets |
| Exact flags for any command | sbx <cmd> --help — current, and authoritative over this page |
| The v1→v2 mapping, version-gated behaviour, overwrite traps | references/discrepancies.md |
| Anything else | the official docs; append .md to the URL |
Something fails → sbx policy log names the rule → sbx policy allow network <host> →
retry. That cycle resolves most of what goes wrong day to day.
Start on the Balanced preset: deny by default, common dev services allowed. When a
setup becomes routine, move it into a kit so it applies at creation instead of by hand —
see creating-sbx-kits.
Both passes cover the same official docs and agree wherever they overlap, so there are no
cross-source conflicts. There is one significant internal inconsistency: the Build your
own agent kit tutorial in agents.md is written entirely in schemaVersion: "1" while the
spec reference in kits.md says to use "2" for new kits — and v1 fields inside a v2 spec
are decode errors, not silent upgrades. Trust kits.md for field names; take the
tutorial for its reasoning. Kits, sbx secret set-custom and shared agent skills are all
labelled experimental, so pin the sbx version for anything you distribute.
For the full v1→v2 field mapping, the version-gated behaviors, the two "the sandbox overwrites this file" traps, and what the scrape dropped, see references/discrepancies.md.
Ranked by how often they cause a confusing failure.
sbx policy allow changes
nothing, look for Governance: Managed by <org> in sbx policy ls.-- replaces the agent's default flags; a -flag appends. This
is how bypass-permission modes get silently dropped.--kit, --clone, --publish and volumes are create-time only. Re-attaching
ignores them silently. Use sbx kit add / sbx ports, or recreate.sh, not bash, in setup.install. And curl | bash swallows failures.user: "1000" for anything under
/home/agent/.serviceDomains narrow. Mapping *.example.com pushes the proxy into
TLS-intercepting mode for every subdomain — including binary CDNs an installer pulls
from, corrupting those downloads. List only hosts that need auth.HTTP_PROXY/HTTPS_PROXY/NO_PROXY in a kit. Overriding routes traffic
away from the forward proxy, killing policy enforcement and credential injection.
Configure an upstream corporate proxy on the host (DOCKER_SANDBOXES_PROXY).update-ca-certificates (PEM, .crt). Do not
point SSL_CERT_FILE at only your CA — it replaces the system bundle.PROXY column in
sbx policy log. Requests on the transparent path get policy but no credential
injection — typical inside a nested Docker container.--all-sandboxes registry credentials need a recreate. Only
sandbox-scoped entries reach a sandbox that already exists..gitignored files — .env is readable..git pointer cannot resolve — edits work, commits do not.--no-share-skills at creation opts out.sbx reset deletes stored secrets unless you pass --preserve-secrets.DOCKER_SANDBOXES_ROOT_SIZE
(20 GB), DOCKER_SANDBOXES_DOCKER_SIZE (/var/lib/docker, 50 GB sparse),
DOCKER_SANDBOXES_CLONED_WORKSPACE_SIZE (clone mode).kit.allowedSources),
because kit install commands run as root. Prefixes match on path-segment boundaries,
so github.com/docker/ does not admit github.com/docker-evil/.Verified against the sbx docs of 2026-08-07 (re-fetched unchanged on 2026-08-10). sbx --help
and sbx <cmd> --help outrank
every command list here. Check sbx --version before trusting anything version-bound: the product
reached a second kit schema and first-class MCP management within four months of launch.
Reference prose is machine-summarized, so some inline shell snippets were dropped — most visibly
every agent page's default startup command block. Confirm a literal command against the
docs.docker.com URL before quoting it, and re-check
references/discrepancies.md after any refresh. finding-sbx-docs
rebuilds the mirror and reports what changed.
cd8f798
If you maintain this skill, you can claim it as your own. Once claimed, you can manage eval scenarios, bundle related skills, attach documentation or rules, and ensure cross-agent compatibility.