1.2

docker-agent run --sandbox, one run end to end

One recorded docker-agent run --sandbox shows the kit, the sandbox, and the allowlist, and one sbx exec into the same VM shows the model call through the proxy.

You have an agent file with the filesystem and shell toolsets, and its model is ai/qwen3:4b on Docker Model Runner on your Mac. On the host, shell runs with your permissions. --sandbox moves the tools into a microVM and leaves the model on the host.

This section follows the recorded run, step by step, and figure 1.3 draws it. When you finish this section, you can read the launch summary of a sandboxed run and explain the two failures this run met.

Fig. 1.3one docker-agent run --sandbox, step by stepsequence
One docker-agent run --sandbox, step by step Seven lifelines: the operator, docker-agent v1.149.0 on the host, the .m101 state directories in the workspace, sbx with sandboxd, the sandbox VM, the host proxy on port 3128, and Docker Model Runner on port 12434. Fifteen rows: sandbox allow writes localhost:12434 to the persistent allowlist; run --sandbox --exec stages the kit sandbox-kits/<hash>, asks sbx to create docker-agent-<hash> from docker-agent-sbx-templates:latest, the sandbox becomes running with 10 CPUs and 32 GiB, and docker-agent allows models.dev and localhost:12434 on the proxy. Inside the VM the session store fails with readonly database (1032). An sbx exec then runs docker-agent inside with its data in /tmp/m101; its model call goes to host.docker.internal:12434 through the proxy, which forwards it to localhost:12434; the answer streams back as chat.completion.chunk events and the operator reads README.md with 1 line. sbx rm --force removes the sandbox. operator your shell docker-agent v1.149.0 .m101 dirs in workspace sbx and sandboxd sandbox VM the guest proxy :3128 on the host Model Runner :12434 1 sandbox allow localhost:12434 2 + localhost:12434 persistent allowlist 3 run --sandbox --exec files-sandbox.yaml 4 sandbox-kits/<hash> the auto-kit 5 create docker-agent-<hash> docker-agent-sbx-templates:latest 6 created, running cpu 10, memory 32 GiB 7 allow models.dev and localhost:12434 8 readonly database (1032) creating session store 9 sbx exec docker-agent run data dirs in /tmp/m101 10 host.docker.internal:12434 through HTTP_PROXY 11 localhost:12434 the sandbox allow rule 12 chat.completion.chunk the streamed answer 13 README.md with 1 line exit 0 14 sbx rm --force 15 removed
docker-agent stages a kit, has sbx create the VM, and opens two hosts on the proxy, and the model call from inside then leaves through that proxy. Read top to bottom, one numbered row per step, and row 8 is the failure that ends the run. Rows 1 to 8 come from the allow, run, and kit-cache captures. Rows 9 to 15 come from the inside run and the removal captures. The chunk name in row 12 is the stream format of the cassette.

The agent file

The agent file differs from the host version only in its provider. The 127.0.0.1 of your host cannot be reached from inside the sandbox, so the provider points at host.docker.internal:

the provider block of the sandboxed agent fileyamlFull log
version: "16"

providers:
  host-runner:
    provider: dmr
    base_url: http://host.docker.internal:12434/engines/llama.cpp/v1

models:
  local:
    provider: host-runner
    model: ai/qwen3:4b
    temperature: 0
…
Cut to the version, the provider, and the model. The agent, its instruction, and its two toolsets are cut.

Step 1: allow the model host

Persistent allowlist: the hosts that docker-agent sandbox allow stores, which docker-agent adds to the allow rules of the sandbox proxy on every later --sandbox run. The help names it the fix for a Blocked by network policy 403. The proxy translates host.docker.internal to localhost, so the capture allows localhost:12434 before the run:

one host added to the persistent allowlisttextFull log
$ docker-agent sandbox allow localhost:12434  (--config-dir, --data-dir, --cache-dir under fixtures/repo-sandbox/.m101, inside the workspace)
…
Added 1 host(s) to the persistent sandbox allowlist:
  + localhost:12434
[exit 0]
…
The welcome banner and the telemetry notice of the first run are cut, and so is the second command.

Step 2: keep the state directories in the workspace

The capture kit keeps docker-agent out of ~/.cagent with --data-dir, --cache-dir, and --config-dir. With --sandbox, a data directory outside the workspace is refused, and the help gives only the default, ~/.cagent. The capture kit therefore puts all three under .m101/ in the workspace:

a data directory outside the workspacetextFull log
…
Error: --data-dir must be inside the sandbox's writable workspace $CAPTURE/fixtures/repo-sandbox
[exit 1]
The command line is cut.

Step 3: the launch summary

the launch summary of docker-agent run --sandboxtextFull log
…
Models gateway: none configured
Models catalog: allowlisting models.dev in the sandbox proxy
User sandbox allowlist: allowlisting 1 host(s) from `docker agent sandbox allow`:
  - localhost:12434

sandbox    docker-agent-<hash>
agent      docker-agent
workspace  $CAPTURE/fixtures/repo-sandbox (rw)
           $CAPTURE/fixtures/agents (ro)
           $CAPTURE/fixtures/repo-sandbox/.m101/cache/sandbox-kits/<hash> (ro)
           $CAPTURE/fixtures/repo-sandbox/.m101/cfg (ro)
image      docker/docker-agent-sbx-templates:latest
cpu        10
memory     32 GiB
…
✓ Created sandbox docker-agent-<hash>
The command line and the image pull progress are cut. The rest of the output follows in step 5.

The run set no --models-gateway. It allows models.dev, because without that host the first catalog lookup fails with 403 Blocked by network policy. The next line repeats the host from step 1.

Auto-kit: a directory that docker-agent stages on the host and mounts read-only into the VM at the same path. Here it is .m101/cache/sandbox-kits/<hash>, and its manifest.json holds only agent_ref and built_at (the capture). The workspace is read-write, and the agent file's directory, the kit, and the config directory are read-only.

Step 4: an ordinary sandbox

During the run, sbx ls lists docker-agent-<hash> with the agent docker-agent, the status running, and the same four workspaces, three of them :ro (the capture). The sandbox is an ordinary one, and sbx lists and removes it like any other.

Step 5: the session store fails

Inside the VM, the run stopped before any model call:

the end of the sandboxed runtextFull log
…
Error: creating session store: migration failed even after database reset: failed to create migrations table: attempt to write a readonly database (1032)
Error: 
[exit 1]
The two self-update warnings before the error are cut.

The data directory sits on the virtiofs workspace mount, which mount lists as rw. A Python sqlite3 write on the same mount fails the same way (the capture), so on this host SQLite cannot write through that mount. The run printed no safety mode, so conflict C62 stays open. Docker Agent exits, but it does not stop or remove the sandbox VM, and the VM stayed running.

Step 6: the model call from inside

the agent binary and the proxy settings inside the VMtextFull log
docker-agent version main
Commit: 154b78f2d517dae1397866dfb7586c4a302a3151
…
ANTHROPIC_API_KEY=proxy-managed
…
HTTP_PROXY=http://gateway.docker.internal:3128
…
NO_PROXY=localhost,127.0.0.1,::1,gateway.docker.internal
OPENAI_API_KEY=proxy-managed
…
Persistent sandbox allowlist is empty.
Cut to the version, four environment lines, and the in-VM allowlist. The other variables and the self-update warnings are cut.

The template runs docker-agent version main, not v1.149.0, although the docs say that :latest is built from the most recent v* release. The provider keys read proxy-managed. The in-VM allowlist is empty, because the allowed hosts arrive as proxy rules from the host.

The capture then starts the same agent file inside the VM by hand, with its state in /tmp/m101. This run shows the model path from the VM, not a working --sandbox launch:

the model call from inside the VMtextFull log
$ sbx exec docker-agent-<hash> sh -c 'docker-agent --config-dir /tmp/m101 --data-dir /tmp/m101 --cache-dir /tmp/m101 run --exec --last …
…
The working directory contains README.md with 1 line.
[exit 0]
The command line is cut after the state flags, and the two self-update warnings are cut.

The request to host.docker.internal:12434 leaves through HTTP_PROXY, because that host is not in NO_PROXY. The sandbox proxy changes host.docker.internal to localhost before it forwards the request, and the rule from step 1 admits it. The answer is correct: the capture kit copies the same README into every workspace, and it holds the one line hello (the capture).

Step 7: remove the sandbox

the removaltextFull log
$ sbx rm --force docker-agent-<hash>
Deleting sandbox docker-agent-<hash>...
Sandbox 'docker-agent-<hash>' removed
[exit 0]
Nothing is cut.

A later run from the same workspace reuses the VM, and creates a new one only when the set of mounts has changed.

Docker Sandboxes (sbx) and Docker Agent (docker-agent) sbx 0.47.0, docker-agent 1.149.0 · bf4169c · 2026-10-07 · Edition 2026.10