Sitemap

Docker Sandboxes: Running Claude Code and Other Coding Agents in Isolated MicroVMs

27 min readOct 19, 2026

--

A beginner-friendly, hands-on guide to giving AI coding agents full autonomy without handing them the keys to your laptop.

Press enter or click to view image in full size

Introduction: the “YOLO mode” dilemma

Docker Sandboxes let you run Claude Code, Codex, Copilot CLI, Gemini CLI, Kiro and other coding agents with full autonomy inside a disposable microVM, so a mistake or an attack stays inside the box instead of reaching your machine.

If you have used Claude Code for more than a day, you know the feeling. Every few seconds it stops and asks: “Can I run npm install?", "Can I edit this file?", "Can I run the tests?". You click Yes forty times. Eventually you discover the --dangerously-skip-permissions flag (fondly called "YOLO mode") and the agent suddenly flies.

Then a small voice asks: what exactly did I just allow? The agent can now run any shell command, as you, on your laptop, with your SSH keys, cloud credentials and browser cookies sitting a few folders away.

This article is about resolving that tension properly. By the end you will understand:

  1. Why autonomous coding agents need isolation, and why a normal container is not quite enough.
  2. What containers, virtual machines and microVMs are, explained from first principles.
  3. How Docker Sandboxes are built: the microVM, the private Docker engine, the network proxy and the credential injector.
  4. How to use them: installing the sbx CLI, running Claude Code, locking down the network, managing secrets, working with Git, publishing ports and running agents in CI.
  5. What to watch out for: the gotchas that still matter even inside a sandbox.

Every command is followed by a plain-English explanation of what it does and why, so you can follow along even if you are new to Docker. All diagrams in this article were created specifically for it.

Note on versions: Docker Sandboxes moves fast. This guide uses the standalone sbx CLI as documented in October 2026. Older tutorials use docker sandbox ... commands from the Docker Desktop integration, which Docker has since deprecated. If a command here differs from an older blog post, the sbx form is the current one.

1. Why coding agents need a sandbox

A coding agent is not a chatbot that suggests code; it is a program that executes code, and anything it executes runs with your permissions.

When you ask Claude Code to “fix the failing tests”, it typically reads files, edits them, installs packages, runs the test suite, maybe starts a database in Docker, and repeats until the tests pass. Each of those steps is a real process on a real operating system. On your laptop, that operating system is the same one that holds everything you care about.

What can go wrong

The risks fall into four buckets. None of them require the model to be “evil”; most come from honest mistakes or from untrusted text the agent reads.

Press enter or click to view image in full size

Why permission prompts don’t scale

The default answer to these risks is to make the agent ask before acting. That works for a five-minute session, but it fails in three ways:

  • Approval fatigue. After the twentieth prompt, people stop reading and click “Yes”. A safety control that is always approved is not a control.
  • No unattended work. The real productivity jump comes from letting an agent work for an hour on its own, or running several agents in parallel. Prompts make that impossible.
  • Wrong granularity. Approving npm test tells you nothing about what the test scripts actually do.

The better approach is the one operating systems have used for decades: don’t ask the program to behave, put it somewhere it cannot do damage. Give the agent a full computer of its own, let it do whatever it wants in there, and control only the narrow doorways between that computer and yours: which files it can see, which websites it can reach, and which credentials it can use.

That “computer of its own” is exactly what Docker Sandboxes provide. To understand why they use microVMs rather than ordinary containers, we need a short detour into how isolation works.

2. Isolation 101: processes, containers, VMs and microVMs

The strength of a sandbox depends on one question: if the code inside misbehaves, what is the next layer it must break through to reach your machine?

Press enter or click to view image in full size

Level 0: a plain process

When you run claude in your terminal, it is just a process: a running program managed by your operating system's kernel (the core part of the OS that controls memory, files, network and hardware). A process inherits your user account. Whatever you can read, delete or upload, it can too. There is no wall at all.

Level 1: a container

A container (what docker run gives you) is still a normal process, but the kernel shows it a filtered view of the world using two Linux features:

  • Namespaces give the process its own private view of the filesystem, process list, network interfaces and hostname. It “thinks” it is alone on the machine.
  • cgroups (control groups) limit how much CPU, memory and I/O it may use.

This is excellent for packaging and running trusted applications. But every container on the machine still talks to the same host kernel. The Linux kernel exposes hundreds of system calls, and a bug in any of them can, in the worst case, let code escape from a container to the host. Containers are a fence, not a wall.

There is also a very practical problem for coding agents: they often need to run Docker themselves (docker build, docker compose up). The two usual workarounds are both poor:

Press enter or click to view image in full size

Level 2: a virtual machine

A virtual machine (VM) goes further. A piece of software called a hypervisor uses hardware virtualization features built into modern CPUs (Intel VT-x, AMD-V, Apple’s Virtualization framework) to create a fake computer: virtual CPU, virtual memory, virtual disk. That fake computer boots its own kernel. Code inside the VM talks to its kernel, not yours. To reach your host, an attacker must find a bug in the hypervisor itself, which is a much smaller and much more heavily scrutinized surface.

The downside of a classic VM is weight: it emulates lots of hardware, takes tens of seconds to boot and uses gigabytes of memory.

Level 3: a microVM

A microVM keeps the strong part of a VM (separate kernel, hypervisor boundary) and throws away the heavy part. It emulates only the handful of devices a server workload needs, boots a minimal kernel, and starts in a fraction of the time. The same idea powers serverless platforms that run untrusted customer code side by side.

For coding agents, a microVM is the sweet spot:

  • Strong isolation: separate kernel, hardware-enforced boundary.
  • Feels like a real machine: the agent can apt install, run services, and even run its own Docker engine, because inside the VM it really is a full Linux system.
  • Disposable: delete it and everything the agent did inside is gone.

The trade-off is a bit more memory and disk than a container, plus a short boot. For an agent that will work for minutes or hours, that is a very good deal.

Key idea to remember: a container hides things from a process; a microVM gives the process a different computer.

3. What Docker Sandboxes actually is

Docker Sandboxes is a tool from Docker that gives each AI coding agent its own disposable microVM, with its own filesystem, network and Docker daemon, so the agent can run unattended without touching your host.

Docker started the project to answer a simple question developers kept asking: how do I run Claude Code or Gemini CLI safely? The first preview shipped as a docker sandbox command inside Docker Desktop and used containers. The current generation, which launched in 2026, moved to one microVM per sandbox and to a standalone CLI called sbx. You no longer need Docker Desktop to use it.

The building blocks

Press enter or click to view image in full size

Which agents are supported

Out of the box the docs list Claude Code, Codex, GitHub Copilot CLI, Cursor, Devin, Docker Agent, Droid, Gemini CLI, Kiro and OpenCode, plus a plain shell agent if you just want an isolated Linux box. Switching agents is one word: sbx run claude, sbx run codex, sbx run gemini and so on. Everything in this article works the same regardless of which agent you pick; I'll use Claude Code in the examples.

Local and cloud sandboxes

Sandboxes normally run locally, on your laptop’s own virtualization. Docker also offers cloud sandboxes (sbx --cloud ...) that run the same model on Docker's infrastructure, which is handy for CI runners without nested virtualization or for long-running jobs you want to close your laptop on. This guide focuses on local sandboxes, since that is where most people start.

Why Claude Code runs in “YOLO mode” by default

Here is the part that surprises people. Inside a sandbox, Claude Code starts with --dangerously-skip-permissions by default. That is not reckless; it is the whole point. The permission prompts existed because the agent was running on your real machine. Once the environment is the safety boundary, the prompts become friction with no benefit, so Docker turns them off and lets the agent work at full speed.

4. Under the hood: how a sandbox is built

A sandbox is one microVM plus a few host-side gatekeepers; Docker’s docs describe five isolation layers: hypervisor, network, Docker engine, workspace and credentials.

Press enter or click to view image in full size

Let’s walk through the diagram one box at a time.

Layer 1: the hypervisor (the outer wall)

Every sandbox is a lightweight VM with its own Linux kernel, created with your operating system’s native virtualization. Processes inside are invisible to your host and to other sandboxes. When you delete the sandbox with sbx rm, the VM and everything in it are destroyed.

One subtle but important point: inside the VM, the agent runs as a non-root user that has sudo. Docker deliberately does not rely on in-VM permissions for safety. The agent can be root inside its own little computer; what matters is that the computer is not yours.

Layer 2: the private Docker engine

Each sandbox runs its own Docker engine inside the VM. When the agent types docker build or docker compose up, those commands go to that engine. There is no path to your host's Docker daemon, so the agent cannot see, stop or delete your own containers and images. This is what makes Docker Sandboxes different from "run the agent in a container": the agent gets full Docker powers without the socket-mount problem we saw earlier.

The flip side: sandboxes don’t share image layers with each other or with your host. Each one pulls and caches its own images, so expect some extra disk usage.

Layer 3: the workspace (the only shared folder)

By default, sbx run shares your current directory into the VM through a filesystem passthrough (virtiofs). The folder appears inside the VM at the same absolute path as on your host, so error messages and stack traces point to paths you recognize. Changes are instant in both directions; there is no sync step. Everything else on your disk (home directory, SSH keys, other repos) simply does not exist inside the VM. Section 8 covers safer alternatives to this read-write mount.

Layer 4: the network proxy

The sandbox has its own isolated network. It cannot talk to other sandboxes or directly to your host network. All outbound TCP traffic is routed through a proxy that runs on your host, and that proxy checks every destination against your network policy. Outbound UDP is off by default, ICMP (ping) is blocked, and DNS goes through a resolver that also enforces the policy. If a domain is not allowed, the connection simply fails.

Layer 5: credential injection

This is the cleverest part. Your API keys are never copied into the VM. Instead, the agent sees a placeholder (a “sentinel”) value. When it sends a request to, say, the Anthropic API, the request leaves the VM, the host proxy checks the policy, looks up the real key in your local secret store, and swaps it into the request header on the way out.

Press enter or click to view image in full size

Why this matters: even if a malicious package runs inside the sandbox and dumps every environment variable and file it can find, there is no real key to steal. The worst it can do is make requests through the proxy, and those requests are still limited by the network policy. Note the difference, though: the key can’t be stolen, but it can still be used. Whatever a token is allowed to do, a tricked agent can do too, which is why GitHub access Section recommends scoping every key to the single job it is for.

Lifecycle: sandboxes are persistent, not ephemeral

A common misconception is that a sandbox is thrown away when the agent exits. It is not. Installed packages, pulled images and agent history survive sbx stop and come back on the next sbx run. A sandbox is deleted only when you run sbx rm (or start it with --rm for a throwaway session).

How it compares

Press enter or click to view image in full size

5. Hands-on: your first sandboxed Claude Code session

Four commands take you from nothing to Claude Code running in a microVM: install sbx, sign in, open a project, and run sbx run claude.

Press enter or click to view image in full size

Prerequisites

  • A 64-bit machine with hardware virtualization enabled (on by default on almost all modern Macs and PCs; on Linux, KVM must be available).
  • A free Docker account (you sign in once with sbx login).
  • For Claude Code: a Claude subscription (Pro, Max, Team or Enterprise) or an Anthropic API key.
  • Docker Desktop is not required.

Step 1: install the sbx CLI

Pick the line for your operating system:

# macOS (Homebrew)
brew install docker/tap/sbx

# Windows (PowerShell)
winget install -h Docker.sbx

# Linux (Debian/Ubuntu): add Docker's apt repo, then install
curl -fsSL https://get.docker.com | sudo REPO_ONLY=1 sh
sudo apt-get install docker-sbx

What is happening here? On macOS, docker/tap/sbx tells Homebrew to fetch the formula from Docker's own "tap" (a third-party package repository) instead of Homebrew's main catalog. On Windows, winget is the built-in package manager and -h means "install silently". On Linux, the get.docker.com script with REPO_ONLY=1 only registers Docker's package repository with apt; it does not install Docker Engine. The second line then installs the docker-sbx package from that repository.

Check that it worked:

sbx --help

Step 2: sign in to Docker

sbx login

This opens your browser for Docker’s OAuth sign-in. Signing in gives each sandbox a verified identity, which Docker uses to pull agent images and, in companies, to apply organization-wide policies. Docker states that it does not read your prompts or monitor sessions.

The first time you sign in (or the first time you run a sandbox), the CLI asks you to choose a default network policy:

Initialize the global network policy for your sandboxes:

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.

Choose Balanced for now. Section 6 explains all three in detail.

Step 3: run Claude Code in a sandbox

cd ~/my-project
sbx run --name my-sandbox claude

Let’s break this command down word by word:

The first run takes a little longer because the agent image is downloaded. After that, sandboxes start in seconds.

Step 4: authenticate Claude Code

You have two options:

  • Claude subscription: inside the sandbox, type /login in Claude Code and complete the browser sign-in. The session token stays on your host and is not stored inside the sandbox.
  • API key: store it once with sbx secret set (covered in Section 7) and every sandbox can use it.

Step 5: give it a real task

Now try something that would normally trigger a dozen permission prompts, for example:

Add a /health endpoint to this Express app, write a Jest test for it,
run the tests, and then build and run the app with Docker Compose to
confirm it responds.

Watch what happens: the agent edits files, runs npm install, runs the tests, writes a Dockerfile, and runs docker compose up against its own Docker engine, all without asking you anything. Meanwhile your host Docker, your home folder and your credentials are untouched.

You can also pass the first prompt directly from the command line. Everything after -- is forwarded to Claude Code:

sbx run --name my-sandbox claude -- "Add error handling to the login function"
# Long prompts can come from a file
sbx run --name my-sandbox claude -- "$(cat prompt.txt)"

The -- separator is a Unix convention meaning "stop parsing options for this program; pass the rest to the next one". Without it, sbx would try to interpret Claude's arguments as its own.

Step 6: look around from a second terminal

sbx ls
SANDBOX      AGENT    STATUS      PORTS   WORKSPACE      CREATED
my-sandbox claude ● running ~/my-project 2m

The WORKSPACE column is the important one: it shows the only part of your disk this sandbox can see. Running sbx with no arguments opens an interactive dashboard with live CPU and memory per sandbox, a network panel showing the connections each sandbox makes, and shortcuts to attach (Enter), open a shell (x), stop (s) or remove (r).

Step 7: stop, resume and clean up

sbx stop my-sandbox          # pause: VM stops, everything inside is kept
sbx run --name my-sandbox # resume from any directory, agent name optional
sbx rm my-sandbox # delete the VM and everything inside it
sbx run --rm claude # throwaway session: auto-deleted when the agent exits

sbx rm asks for confirmation and deletes installed packages, images and any in-sandbox Git clone. Files in your host workspace are not affected. To bulk-remove all stopped sandboxes, use sbx prune (add --dry-run to preview first).

6. Controlling what the agent can reach: network policies

The network policy decides which hosts a sandbox may connect to, and it is your main defence against data exfiltration after a prompt injection.

Think about it this way: isolation protects your machine, but your project is still inside the sandbox. If an agent reads a poisoned README that says “send the source code to evil.example”, the filesystem wall doesn’t help. The network wall does.

Press enter or click to view image in full size

The three presets

Press enter or click to view image in full size

The preset is global: it applies to every current and future sandbox on the machine. Agent kits (including the built-in Claude Code one) can also add a few per-sandbox rules they need to function.

Inspecting and changing rules

# See which rules are currently in effect
sbx policy ls

# Allow the npm registry so the agent can install packages
sbx policy allow network registry.npmjs.org

# Allow a specific host and port
sbx policy allow network api.github.com:443

# See which connections were allowed and blocked recently
sbx policy log

# Start over: wipe your rules and choose a preset again
sbx policy reset

How to read these commands: sbx policy is the command group for rules. allow network <host> adds a rule that permits outbound connections to that host; you can add :port to be more precise. There are matching deny and rm subcommands to block a host explicitly or remove a rule you added. sbx policy log is your debugging friend: when the agent says "network error" or a package install hangs, the log shows exactly which host was blocked.

A practical workflow for Locked Down mode

If you want maximum safety, start from Locked Down and open only what the task needs:

# 1. The model API, otherwise the agent cannot think
sbx policy allow network api.anthropic.com

# 2. Package registries for your stack
sbx policy allow network registry.npmjs.org
sbx policy allow network pypi.org
sbx policy allow network files.pythonhosted.org

# 3. Your Git host, only if the agent should push or open PRs
sbx policy allow network github.com

# 4. Run the agent, then check what else it tried to reach
sbx run claude
sbx policy log

This “start closed, open as needed” pattern is called an allowlist, and it is the same approach firewalls in production networks use. The benefit is that a surprise destination (for example a paste site or an unknown IP) shows up in the log as blocked instead of quietly succeeding.

Get Vasanthan K’s stories in your inbox

Join Medium for free to get updates from this writer.

You can also manage rules visually: run sbx, press Tab to switch to the network panel, and allow or block hosts straight from the live connection list.

Organization policies

In a company, administrators can define network and filesystem policies centrally in the Docker Admin Console. Those organization rules take precedence over the local rules you set with sbx policy, so a developer cannot accidentally (or deliberately) loosen what security has decided.

7. Credentials and secrets: keys that never enter the box

Store API keys and tokens with sbx secret set, never as environment variables or files in the sandbox, so the host proxy can inject them without the agent ever seeing the real value.

Storing an Anthropic API key

sbx secret set anthropic

The command prompts you for the key (so it doesn’t end up in your shell history) and stores it in a secret store on your host. anthropic is the name of a built-in service: the CLI knows which hosts that key belongs to, so the proxy only adds it to requests going to Anthropic's API, never to other destinations.

If you use a Claude subscription instead of an API key, skip this and run /login inside Claude Code. The OAuth token also stays on the host.

Giving the agent GitHub access

# Recommended: a fine-grained token limited to one repository
sbx secret set github

# Quick but broad: reuses your personal GitHub CLI token
sbx secret set github --command 'gh auth token'

The first form prompts you to paste a token, so it never lands in your shell history. Create it on GitHub under Settings → Developer settings → Personal access tokens → Fine-grained tokens, select only the repository the task needs, grant only the permissions it uses (for example Contents and Pull requests: read and write), and set an expiry date.

The second form is convenient: sbx obtains the token by running gh auth token (the GitHub CLI command that prints your current token). But that token usually covers every repository you can access, so a prompt-injected agent could push to any of them. Keep it for quick experiments on your own throwaway repos.

Either way, the token itself never lives inside the VM; the proxy adds it to requests to GitHub.

Scope every key to the job

Credential injection stops a key from being stolen, but not from being used. Whatever a token allows, an agent tricked by a prompt injection can do too. So treat each key as the agent’s budget:

Press enter or click to view image in full size

Narrow tokens and tight network rules cover each other’s gaps: the policy limits where requests go, and the token limits what they can do once they get there.

Importing many secrets at once

sbx secret import --all

This scans your current environment for the variables each built-in service expects (for example ANTHROPIC_API_KEY) and imports them into the secret store in one go. It is especially useful on CI runners, where secrets are already provided as environment variables.

Environment variables: for configuration, not secrets

Sometimes the agent needs ordinary settings. Use -e for those:

# Set a value directly
sbx run -e LOG_LEVEL=debug claude

# Copy a value from your host environment
export API_URL=https://api.example.com
sbx run -e API_URL claude

# Load several from a file
sbx create --name my-project --env-file .env.sandbox claude .

The rule of thumb: anything you would be upset to see leaked goes into sbx secret; everything else can be an environment variable. Environment variables are readable by every process inside the sandbox, including a malicious postinstall script. Secrets handled by the proxy are not.

Two caveats worth knowing

  • SSH agent forwarding is on by default. If your host has an SSH agent running, its socket is forwarded so the agent can sign Git commits with your key. The private key never leaves your host, but any process inside the sandbox can ask your SSH agent to sign or authenticate while the sandbox runs. If that worries you, disable forwarding or use a dedicated key.
  • User-level agent config is not copied in. Your ~/.claude folder (global settings, personal skills) stays on the host. Only project-level configuration inside the workspace, such as .claude/ in the repo, is visible to the sandboxed agent. This is intentional: global config often contains tokens and personal hooks.

8. Workspaces and Git: direct, clone and mountless modes

The default direct mount lets the agent write straight into your working tree; clone mode (--clone) gives it a private copy instead, which is the safer choice for unfamiliar agents and parallel work.

Press enter or click to view image in full size

Mode 1: direct mount (the default)

git checkout -b feat/health-endpoint
sbx run claude
# ...agent works...
git diff
git add -p
git commit -m "feat: add /health endpoint"

The sandbox mounts your working tree read-write. Every file the agent saves appears instantly on your host, and you review it with ordinary Git tools. git add -p ("patch mode") is worth learning here: it walks you through each change hunk by hunk and asks whether to stage it, which forces a real review instead of a blind git add ..

The risk to understand: in direct mode there is no wall between the agent’s edits and your files. It can also modify files that execute code later, on your host, when you do normal things:

Docker’s own advice is to treat sandbox-modified files like a pull request from an untrusted contributor. A quick habit after each session:

git status                 # anything unexpected changed?
git diff # read the actual changes
ls -la .git/hooks/ # hooks are invisible to git diff, check them separately

Mode 2: clone mode (recommended for autonomy)

sbx run --clone --name health claude .

With --clone, your repository is mounted read-only at /run/sandbox/source inside the VM, and the agent works on a private Git clone that lives only in the sandbox. Even with root inside the VM, it cannot modify a single byte of your working tree or your .git folder.

When the agent is done, you pull its work like you would from any other remote:

# Ask the agent first: "create a branch feat/health and commit your changes"

git fetch sandbox-health # sbx registered this remote for you
git log sandbox-health/feat/health # see its commits
git diff main..sandbox-health/feat/health # review everything

# Happy? Bring it home and open a PR
git checkout -b feat/health sandbox-health/feat/health
git push -u origin feat/health
gh pr create

How this works behind the scenes: the sandbox runs a small Git daemon that publishes its clone, and sbx adds a remote named sandbox-<name> to your host repository that points at it. Fetching from that remote is exactly as safe as fetching from a colleague's fork: nothing is merged until you merge it. A few things to know:

  • --clone is fixed when the sandbox is created; to switch modes, remove and recreate.
  • The clone starts from whatever branch you have checked out; no branch is created automatically, so ask the agent to create one.
  • The Git daemon only runs while the sandbox runs, so git fetch fails after sbx stop.
  • sbx rm deletes the clone. Fetch or push anything you want to keep first.
  • Read-only does not mean invisible: untracked files such as .env in the repo are still readable by the agent. Keep secrets out of the working tree.

Running several agents in parallel

Clone mode shines when you want several tasks done at once. One clone-mode sandbox can hold many branches, and Claude Code’s agents view can dispatch background sessions:

sbx run --clone claude . -- --dangerously-skip-permissions agents

Why the long flag? When the first argument after -- is a bare word like agents, it replaces the default startup command, so the default --dangerously-skip-permissions would be lost. Passing it explicitly keeps the agents in autonomous mode. To keep tasks separated, add a line to your project's CLAUDE.md such as "Always start each task on its own git branch." Then fetch all the branches at the end with a single git fetch sandbox-<name>.

Alternatively, give each task its own named sandbox:

sbx run --clone --name feature claude ~/my-project
sbx run --clone --name spike claude ~/my-project

Mode 3: mountless (scratch space)

sbx create --name scratch claude     # no path = nothing shared from the host
sbx run --name scratch # attach to it

# Move individual files in or out (sandbox paths must be absolute)
sbx cp ./config.json scratch:/home/agent/workspace/
sbx cp scratch:/home/agent/workspace/output.log ./

The agent works in /home/agent/workspace inside the VM. This is ideal for "try this library and tell me if it fits" experiments where you don't want anything touching your disk.

Bonus: extra read-only folders

sbx run claude ~/project-a ~/shared-libs:ro ~/docs:ro

The first path is the main (read-write) workspace. Each extra path ending in :ro is mounted read-only, so the agent can read your shared library or design docs for context without being able to change them.

9. Everyday commands you’ll actually use

Beyond run, stop and rm, five commands cover most daily work: exec for a shell, ports to reach dev servers, -d for background sandboxes, template save to reuse a setup, and a different agent name to switch tools.

Open a shell inside a running sandbox

sbx exec -it my-sandbox bash

exec runs a command inside an existing sandbox (just like docker exec). -i keeps input open and -t allocates a terminal, so together -it gives you an interactive shell. It starts in the primary workspace. Use it to inspect what the agent installed, check logs, or run a command yourself.

You can also run a single command without an interactive shell:

sbx exec my-sandbox -- npm test
sbx exec my-sandbox bash -c "docker ps" # see the agent's containers, not yours

That second example is a nice way to see the isolation: docker ps on your host and inside the sandbox show completely different lists.

Reach a dev server running in the sandbox

Sandboxes are network-isolated, so localhost:3000 on your laptop won't reach an app the agent started inside the VM. You publish a port explicitly:

# When creating the sandbox: host port 8080 -> sandbox port 3000
sbx run --publish 8080:3000 --name web claude

# Or on an existing sandbox
sbx ports web --publish 8080:3000
open http://localhost:8080 # macOS; use your browser on other OSes

# Let the OS pick a free host port, then look it up
sbx ports web --publish 3000
sbx ports web

# Stop forwarding
sbx ports web --unpublish 8080:3000

The format HOST:SANDBOX is the same as Docker's -p flag: the left number is the port on your machine, the right one is the port inside the sandbox.

Keep a sandbox running in the background

sbx run -d --name web claude .

-d (detach) starts the sandbox and returns immediately without opening the agent. A detached sandbox keeps running until you stop it, which is useful for serving an app on a published port while you work elsewhere.

Save a configured sandbox as a reusable template

Suppose you spent 20 minutes installing a specific Node version, Playwright browsers and a database client. Instead of repeating that for every new sandbox:

sbx stop my-sandbox
sbx template save my-sandbox my-node-env:v1 # snapshot its filesystem
sbx run -t my-node-env:v1 claude ~/other-project # start a new sandbox from it

sbx template ls # list saved templates
sbx template save my-sandbox my-node-env:v1 --output my-node-env.tar # share as a file
sbx template load my-node-env.tar # import on another machine

Watch out: a template captures everything in the sandbox’s filesystem. If you ever pasted an API key into a file there, it is now inside the template and inside anyone’s copy of it. This is another reason to keep credentials in sbx secret. For a fully reproducible, version-controlled setup, Docker's kits let you declare tools, network rules and the agent in code.

Switch agents

Same sandbox model, different brain:

sbx run codex      # OpenAI Codex CLI
sbx run copilot # GitHub Copilot CLI
sbx run gemini # Google Gemini CLI
sbx run kiro # Kiro
sbx run opencode # OpenCode
sbx run shell # no agent, just an isolated Linux box

Each agent has its own template and its own authentication method (check the agent’s page in the Docker docs). Everything else in this guide, from policies to clone mode, works identically.

Quick reference

10. Running agents headless and in CI

In scripts and CI, the pattern is: log in with a Docker access token, sbx create a clone-mode sandbox, sbx exec the agent with a prompt, fetch its branch, then sbx rm --force.

The four-line pattern

echo "$DOCKER_PAT" | sbx login --username "$DOCKER_USER" --password-stdin

sbx create --name ci-task --clone claude .
sbx exec ci-task -- claude --dangerously-skip-permissions -p "Review the code, fix lint errors and commit your changes"
git fetch sandbox-ci-task
sbx rm --force ci-task

Line by line:

  1. Log in without a browser. A Docker Personal Access Token (PAT, with at least Read scope) is piped in through --password-stdin, so it never appears in the process list or logs.
  2. sbx create builds the sandbox but doesn't attach a terminal (there is nobody to type in CI). --clone means the agent can't modify the runner's checkout directly.
  3. sbx exec ... -- claude -p "..." runs Claude Code in print mode (-p): it executes the prompt to completion and exits instead of opening the interactive UI. Because exec replaces the default command, we pass --dangerously-skip-permissions explicitly.
  4. git fetch sandbox-ci-task pulls the agent's commits onto the runner, where later steps can test them or open a PR.
  5. sbx rm --force cleans up. In scripts, --force matters: confirmation prompts need a real terminal, and piping echo y into them does not work. A command that needs confirmation with no terminal exits with status 1.

A GitHub Actions example

name: agent-lint-fix
on:
workflow_dispatch:
jobs:
fix:
runs-on: ubuntu-latest # needs a Linux runner with KVM available
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Install sbx
run: |
curl -fsSL https://get.docker.com | sudo REPO_ONLY=1 sh
sudo apt-get install -y docker-sbx
- name: Sign in and store the model key
run: |
echo "${{ secrets.DOCKER_PAT }}" | sbx login
--username "${{ secrets.DOCKER_USER }}" --password-stdin
sbx secret set anthropic --force -t "${{ secrets.ANTHROPIC_API_KEY }}"
- name: Let the agent work in a sandbox
run: |
sbx create --name ci-task --clone claude .
sbx exec ci-task -- claude --dangerously-skip-permissions \
-p "Run the linter, fix every error, and commit on a branch named agent/lint-fix"
git fetch sandbox-ci-task
git push origin sandbox-ci-task/agent/lint-fix:refs/heads/agent/lint-fix
- name: Clean up
if: always()
run: sbx rm --force ci-task || true

Things worth explaining in this workflow:

  • fetch-depth: 0 checks out the full Git history, so the clone inside the sandbox has real branches to work with.
  • sbx secret set anthropic --force -t ... stores the key from GitHub's secret store into the sandbox secret store. -t passes the token value; --force overwrites any existing entry without prompting.
  • git push origin sandbox-ci-task/agent/lint-fix:refs/heads/agent/lint-fix uses Git's source:destination refspec syntax: take the branch from the sandbox remote and publish it on origin under a normal branch name. The push runs on the runner, outside the sandbox, so you decide what leaves.
  • if: always() makes the cleanup step run even when an earlier step fails.
  • The runner needs hardware virtualization. If your CI provider doesn’t expose KVM, Docker’s cloud sandboxes (sbx --cloud) run the same workflow without local virtualization.

Machine-readable output

For scripts, never parse the pretty tables; ask for JSON instead:

sbx ls --json | jq -r '.[].name'          # names of all sandboxes
sbx prune --dry-run --format yaml # what would be removed

Results go to stdout and errors to stderr, so jq only ever sees clean data. Note that sbx v0.48.0 changed several flags (for example --detached became --detach/-d) and switched JSON fields to snake_case; update older scripts accordingly.

11. Security checklist, limitations and gotchas

A sandbox shrinks the blast radius, but it does not make an agent trustworthy; the remaining risks sit exactly at the doors you open: the workspace, the network allowlist, and forwarded credentials.

A practical checklist

  • Use Balanced or Locked Down network policy, never Open, for anything beyond a toy project.
  • Store keys with sbx secret set, not -e or .env files inside the workspace.
  • Scope every token to the single repo and API the task needs (fine-grained GitHub token, separate spend-capped model key), so a hijacked agent can only spend what it was handed.
  • Give every token an expiry, ideally one token per run, so it dies with that run instead of sitting in a config for months. In CI, prefer short-lived tokens such as GitHub Actions’ built-in GITHUB_TOKEN, which expires when the job ends.
  • Prefer --clone when running unattended or running several agents.
  • After a direct-mount session, review git diff, .git/hooks/, CI files and build scripts before committing or building on the host.
  • Keep real secrets (.env, cloud credentials) outside the project folder; clone mode still lets the agent read the repo.
  • Check sbx policy log occasionally to see what your agents are actually trying to reach.
  • Remove sandboxes you no longer need (sbx rm, sbx prune) to reclaim disk and reduce stale state.
  • Never save a template from a sandbox where you typed a credential into a file.

Limitations to be aware of

Coming from the old docker sandbox commands?

Early tutorials (late 2025 to early 2026) use the Docker Desktop integration:

# Old, deprecated Docker Desktop integration
docker sandbox run claude
docker sandbox ls
docker sandbox rm <id>

# Current standalone CLI
sbx run claude
sbx ls
sbx rm <name>

The concepts carry over directly; mainly the command prefix changed, and you no longer need Docker Desktop. If you have old sandboxes, recreate them with sbx rather than trying to migrate them.

12. Conclusion

Docker Sandboxes change the safety question from “should I approve this command?” to “what should this computer be allowed to touch?”, which is a question you answer once instead of forty times a session.

The core ideas to take away:

  1. Isolation beats permission prompts. Give the agent its own microVM and let it work at full speed inside.
  2. MicroVMs are stronger than containers. A separate kernel behind a hypervisor is a wall; a shared kernel is a fence.
  3. The doors are where the risk lives. Control the workspace (prefer --clone), the network (Balanced or Locked Down) and credentials (sbx secret, never env vars).
  4. Review like it’s a stranger’s PR. Especially hooks, CI files and build scripts.

If you try only one thing after reading this, make it this:

cd ~/a-repo-you-dont-mind-experimenting-with
sbx run --clone --name first-try claude .

Then ask the agent to do something ambitious, and watch it work without asking for permission once.

--

--

Written by Vasanthan K

Senior Full Stack Engineer | Scalable Enterprise Systems | Node.js • GraphQL • React/Vue • Mongodb | Azure Cloud | 10+ Year | Gen AI enthusiast