CtrlK
BlogDocsLog inGet started
Tessl Logo

running-sbx-sandboxes

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

Quality

92%

Does it follow best practices?

Run evals on this skill

Adds up to 20 points to the overall score

View guide

SecuritybySnyk

Passed

No findings from the security scan

SKILL.md
Quality
Evals
Security

Running sbx sandboxes

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-serverssbx 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.

When to Use This Skill

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.

Key Concepts

TermWhat it means
SandboxA 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.
TemplateA 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.
KitA 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 kitkind: mixin — layers capability onto an existing agent. Must not declare sandbox:, extends:, or mixins:.
Sandbox kitkind: 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 proxyHost-side HTTP/HTTPS proxy. Enforces policy and injects credentials.
Transparent proxyFallback 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-managedThe 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.shThe sandbox's persistent env file, sourced before every bash invocation. Where custom env vars and version-manager init lines go.

The five isolation layers

Hypervisor → network → Docker Engine → workspace → credential proxy. Two consequences trip people up constantly:

  1. Egress is deny-by-default and HTTP/HTTPS-only. Raw TCP, UDP (including DNS) and ICMP are blocked at the network layer; DNS goes through the proxy and obeys policy. Private IP ranges, loopback and link-local are blocked.
  2. Credentials are not in the VM. The host proxy injects them on the way out, so an "invalid API key" error inside the sandbox is usually a routing problem (wrong proxy path, TLS failure) rather than a key problem.

Quick Reference

Lifecycle, naming, shells

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 entirely

sbx 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-stdin

Move 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).

Pass a prompt or flags to the agent — the argument rule

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.

The built-in agents at a glance

AgentAuthenticationTemplate (docker/sandbox-templates:)
claudesbx secret set anthropic, or /login in-session for a Claude subscription (OAuth)claude-code
codexHost-side OAuth (prompted before launch if nothing is stored), or sbx secret set openaicodex
cursorStored API key, or OAuth — the proxy intercepts the exchange at api2.cursor.sh/auth/pollcursor-agent-docker
geminiGoogle API key, or interactive Google sign-in scoped to that sandboxgemini
copilotA GitHub token with Copilot access, via stored secretscopilot
kiroDevice flow in a browser; state in ~/.local/share/kiro-cli/data.sqlite3kiro
droidFactory account — API key or OAuth. Factory brokers model access, so no provider keydroid-docker
opencodeMulti-provider, chosen in the TUI. Zen keys need set-custom + opencode.ai allowedopencode
docker-agentMulti-provider: openai, anthropic, google, xai, nebius, mistral, openrouterdocker-agent
shellNone — no agent binary; runs bash -l. Store secrets first; the proxy still injectsshell
  • 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).

Customizing: kit or template

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 it

Writing either one is a sibling skill: creating-sbx-kits for the spec, and creating-sbx-templates for the image. Both carry worked, commented assets.

Network policy — the command you will run most

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-all

Deny 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.

Non-HTTP egress (SSH) needs an IP, not a hostname

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.

Credentials

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 ls

A 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.

Ports, both directions

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 service

The 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.

Custom environment variables inside the sandbox

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.

Share host agent skills into sandboxes

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 prompting

Importing 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.

AgentHost sourceMounted 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:

PlatformShared 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.

Paste images from the host clipboard

sbx settings set clipboard.imagePaste true

Text 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.

Diagnose

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 state

Try them in that order. sbx reset also clears the template cache, the shared skills store, and — without --preserve-secrets — every stored secret.

Where the detail lives

The Quick Reference above is the 80% case. For anything deeper:

NeedGo to
Write or fix a spec.yamlskill creating-sbx-kits, and its references/spec-fields.md for every field
Write a Dockerfile or a template familyskill creating-sbx-templates
Something blocked, killed, or vanishedskill diagnosing-sbx-sandboxes
Give an agent an MCP server, or govern its tool callsskill wiring-sbx-mcp-servers
Attach VS Code, Cursor, Claude Desktop or ChatGPT over SSHskill connecting-to-sbx-sandboxes
Administer org policy, filesystem rules, or audit loggingskill governing-sbx-fleets
Exact flags for any commandsbx <cmd> --help — current, and authoritative over this page
The v1→v2 mapping, version-gated behaviour, overwrite trapsreferences/discrepancies.md
Anything elsethe official docs; append .md to the URL

The loop

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.

Known Discrepancies

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.

Gotchas Worth Memorizing

Ranked by how often they cause a confusing failure.

  1. Deny beats allow, and org policy beats local allow. If sbx policy allow changes nothing, look for Governance: Managed by <org> in sbx policy ls.
  2. A bare word after -- replaces the agent's default flags; a -flag appends. This is how bypass-permission modes get silently dropped.
  3. --kit, --clone, --publish and volumes are create-time only. Re-attaching ignores them silently. Use sbx kit add / sbx ports, or recreate.
  4. sh, not bash, in setup.install. And curl | bash swallows failures.
  5. Install steps run as root by default. user: "1000" for anything under /home/agent/.
  6. Keep 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.
  7. Never set 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).
  8. A corporate TLS-inspecting proxy breaks credential injection before it starts. Install the internal root CA with update-ca-certificates (PEM, .crt). Do not point SSL_CERT_FILE at only your CA — it replaces the system bundle.
  9. Credentials configured but calls still fail? Check the PROXY column in sbx policy log. Requests on the transparent path get policy but no credential injection — typical inside a nested Docker container.
  10. Global secrets and --all-sandboxes registry credentials need a recreate. Only sandbox-scoped entries reach a sandbox that already exists.
  11. Clone mode protects from modification, not inspection. The repo is mounted read-only including untracked and .gitignored files — .env is readable.
  12. A host worktree gives the agent no Git at all. Only the worktree directory is mounted, so the .git pointer cannot resolve — edits work, commits do not.
  13. The shared skills store is read-write and cross-sandbox. One sandbox can rewrite something another later runs. --no-share-skills at creation opts out.
  14. sbx reset deletes stored secrets unless you pass --preserve-secrets.
  15. Disk is sized by env var at creation, independently: DOCKER_SANDBOXES_ROOT_SIZE (20 GB), DOCKER_SANDBOXES_DOCKER_SIZE (/var/lib/docker, 50 GB sparse), DOCKER_SANDBOXES_CLONED_WORKSPACE_SIZE (clone mode).
  16. Kit installs are restricted to Docker Hub by default (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/.

Last verified

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.

Repository
slurpyb/sbx-agent
Last updated
First committed

Is this your skill?

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.