{"slug": "running-deepagents-in-a-docker-sandbox-with-no-cloud-keys", "title": "Running DeepAgents in a Docker Sandbox, with no cloud keys", "summary": "A developer published a four-file Docker Sandbox Kit that packages the DeepAgents agent harness into an isolated microVM-style sandbox pre-wired to a local Docker Model Runner, letting the agent run with no cloud credentials and no external network egress. The kit's descriptor grants PyPI access only during the install phase and restricts the running agent to the host's Model Runner on port 12434, which speaks the OpenAI wire format so DeepAgents connects via a standard ChatOpenAI client.", "body_md": "Agent frameworks are easy to `pip install` and surprisingly hard to run responsibly. The moment you give an agent a filesystem, a shell, and a network, you have handed arbitrary generated code the same reach your laptop has. You also inherit a second problem that has nothing to do with safety: reproducing the exact environment, the exact package versions, and the exact model wiring on someone else's machine.\n\nThis post walks through a small, self contained answer to both problems: a **Docker Sandbox Kit** that drops the [deepagents](https://github.com/langchain-ai/deepagents) harness into an isolated sandbox, pre wired to a **local Docker Model Runner** so it runs with no cloud credentials at all. The whole kit is four files, and it is published on Docker Hub so you can run it in one command.\n\n[DeepAgents](https://github.com/langchain-ai/deepagents) is an opinionated agent harness built on LangGraph. Out of the box it gives an agent a planning tool, a virtual filesystem, sub agent delegation, and a detailed system prompt. You construct an agent in a few lines:\n\n``` python\nfrom deepagents import create_deep_agent\n\nagent = create_deep_agent(model=my_model, system_prompt=\"...\")\n```\n\nTwo things make it interesting to package. First, it is a library rather than a turnkey CLI, so the useful unit to ship is \"deepagents, installed and wired to a model, ready to import.\" Second, it defaults to a cloud model (Anthropic), which means a naive setup needs an API key and open egress to a provider. We want neither.\n\nDocker Sandboxes (the `sbx` CLI) run agents inside isolated microVM style environments with a credential proxy and an enforced network policy. A **kit** is the unit of composition: a single OCI image whose manifest carries a **descriptor** describing what the kit offers, what it needs from the host as typed capability requests, and what it needs from other kits.\n\nKits come in two kinds:\n\nDeepAgents is a capability you add to an environment, not an environment of its own, so it is a natural **mixin**. It composes onto any workload that carries a Python runtime, for example the stock `docker/sbx-kit-shell` image.\n\n```\n          your host\n  ┌──────────────────────────────────────────────────────────┐\n  │                                                            │\n  │   Docker Model Runner  ──  :12434  (OpenAI-compatible)     │\n  │            ▲                                               │\n  │            │  egress allowed only to                       │\n  │            │  host.docker.internal:12434                   │\n  │   ┌────────┴──────────────  sandbox  ──────────────────┐   │\n  │   │  shell workload  +  deepagents mixin               │   │\n  │   │    • deepagents + langchain-openai (pip, create)   │   │\n  │   │    • OPENAI_BASE_URL / OPENAI_API_KEY pre-wired     │   │\n  │   │    • ~/deepagents_quickstart.py                     │   │\n  │   └─────────────────────────────────────────────────────┘   │\n  └──────────────────────────────────────────────────────────┘\n```\n\nThe sandbox reaches exactly one host at runtime, the Model Runner on port 12434, and nothing else. The model speaks the OpenAI wire format, so deepagents talks to it through a standard `ChatOpenAI` client. No traffic leaves your machine.\n\nThe kit is authored as a companion pair plus its guidance and docs:\n\n```\ndeepagents.yaml          the v3 descriptor\ndeepagents.dockerfile    a FROM scratch overlay that carries runtime ENV\ndeepagents-context.md    agent guidance, staged into the sandbox\nREADME.md\n```\n\nThe descriptor declares the kit's identity, its version, and the capabilities it requests. Here are the parts that carry the design.\n\n**Phase scoped network policy.** Egress is granted per phase, and an absent phase grants nothing. The install phase may reach PyPI; the running agent may reach only the Model Runner. The PyPI grant closes before the agent ever starts.\n\n```\ncapabilities:\n  - type: com.docker.sandbox/network-policy@1\n    config:\n      install:\n        allow: [pypi.org, files.pythonhosted.org]\n      runtime:\n        allow: [host.docker.internal:12434]\n```\n\n**Create time install, not a baked layer.** A mixin overlay lands on a base whose Python version and `site-packages` path you cannot know in advance, so copying a prebuilt package tree into the image would not resolve. Instead the kit installs into the *composed* base's Python at sandbox create time, via lifecycle hooks. The hooks also do two things worth calling out: they fail early with a clear message if the base Python is older than 3.11, and after installing they re read the installed version and fail on mismatch. A pinned version claim is only honest if the build enforces it.\n\n```\n  - type: com.docker.sandbox/lifecycle@1\n    config:\n      install:\n        - command: \"python3 -c \\\"import sys; sys.exit(0 if sys.version_info >= (3, 11) else 1)\\\" || { echo 'deepagents needs Python >= 3.11' >&2; exit 1; }\"\n          user: \"1000\"\n        - command: \"pip install --break-system-packages 'deepagents==0.7.21' langchain-openai\"\n          user: \"1000\"\n          env: [HTTP_PROXY, HTTPS_PROXY]\n        - command: \"python3 -c \\\"import importlib.metadata as m, sys; sys.exit(0 if m.version('deepagents') == '0.7.21' else 1)\\\"\"\n          user: \"1000\"\n```\n\nOne subtlety: hook environments are deny by default, so `HTTP_PROXY` and `HTTPS_PROXY` are declared explicitly. `pip` reads them to fetch through the sandbox's forced proxy; without the declaration the install would hang.\n\n**No hard `requires`.** It is tempting to declare `requires: [\"deb/python3\"]`, but `requires` is a closed set check: a name nothing in the composition provides makes the kit refuse to compose everywhere, and a `deb/` name rules out every Alpine or Wolfi base that would otherwise have worked. The Python 3.11 guard hook above is the better tool: it degrades gracefully with an actionable error rather than refusing up front.\n\n**No credential.** Because the model is the local Model Runner, the API key is the sentinel string `\"dmr\"` rather than a real secret, so the kit declares no `credential@1` capability and asks for nothing from the credential proxy.\n\nThe content recipe is a `FROM scratch` overlay that installs nothing. Its only job is to carry static environment onto the composed image. A mixin's `ENV` is an additive image config field that merges at assembly, so it reaches both the composed image and the agent process.\n\n```\nFROM scratch\n\nENV OPENAI_BASE_URL=\"http://host.docker.internal:12434/engines/v1\" \\\n    OPENAI_API_KEY=\"dmr\" \\\n    DEEPAGENTS_MODEL=\"ai/qwen3\" \\\n    LANGSMITH_TRACING=\"false\"\n```\n\n`LANGSMITH_TRACING=false` keeps LangChain from attempting hosted tracing, which the runtime policy would block anyway, but switching it off avoids the noise. `NO_PROXY` is deliberately not set here, because the shell workload already defines it and two kits setting the same variable to different values is a hard composition conflict.\n\nA lifecycle `files` entry stages a runnable example into the sandbox, marked so it is never overwritten if you have edited it. It passes an explicit `ChatOpenAI` instance to override the Anthropic default:\n\n``` python\nimport os\nfrom langchain_openai import ChatOpenAI\nfrom deepagents import create_deep_agent\n\nmodel = ChatOpenAI(\n    model=os.environ.get(\"DEEPAGENTS_MODEL\", \"ai/qwen3\"),\n    base_url=os.environ[\"OPENAI_BASE_URL\"],\n    api_key=os.environ.get(\"OPENAI_API_KEY\", \"dmr\"),\n    temperature=0,\n)\n\nagent = create_deep_agent(model=model, system_prompt=\"You are a concise assistant. Plan before you act.\")\n\nif __name__ == \"__main__\":\n    result = agent.invoke({\"messages\": [{\"role\": \"user\", \"content\": \"In two sentences, what is a sandbox?\"}]})\n    print(result[\"messages\"][-1].content)\n```\n\nA kit is just an OCI artifact built by `docker buildx`, with the descriptor validated before any content is built. A fast first check is to validate the descriptor without exporting anything:\n\n```\ndocker buildx build . -f deepagents.yaml --output type=cacheonly\n```\n\nThen export an OCI layout and run the conformance suite. The kit passes all 18 checks on both linux/amd64 and linux/arm64, including the overlay ownership checks that catch a mixin accidentally taking over the agent's home directory:\n\n```\ndocker buildx build . -f deepagents.yaml -t deepagents:0.7.21 \\\n  --output type=oci,dest=/tmp/deepagents-layout,tar=false\nkit-tck validate --layout /tmp/deepagents-layout 0.7.21\n```\n\nA build proves the recipe ran, not that the overlay works, so the check that counts is a real composition. Point `--kit` at the kit directory and let `sbx` assemble it onto a shell workload:\n\n```\nsbx run docker/sbx-kit-shell:1.0.0 \\\n  --kit \"$(pwd)\" --name deepagents-demo /path/to/workspace\n```\n\nOn create, `sbx` assembles the two kits, runs the three install hooks, and writes the quickstart file. Inside the sandbox the result is exactly what the descriptor promised:\n\n``` python\n$ sbx exec deepagents-demo python3 -c \"import deepagents; print(deepagents.__version__)\"\n0.7.21\n$ sbx exec deepagents-demo sh -lc 'echo $OPENAI_BASE_URL'\nhttp://host.docker.internal:12434/engines/v1\n```\n\nPublishing a kit is just a build with `--push`. Both platforms go in one invocation so the index that consumers resolve through is written once:\n\n```\ndocker buildx build . -f deepagents.yaml --platform linux/amd64,linux/arm64 --push \\\n  -t docker.io/ajeetraina777/sbx-kit-deepagents:0.7.21 \\\n  -t docker.io/ajeetraina777/sbx-kit-deepagents:latest\n```\n\nOnce it is on Docker Hub, anyone can run it without cloning the repo. First make sure the Model Runner is serving a tool calling model:\n\n```\ndocker model pull ai/qwen3\n\nsbx run docker/sbx-kit-shell:1.0.0 \\\n  --kit docker.io/ajeetraina777/sbx-kit-deepagents:0.7.21 --name deepagents-demo .\n```\n\nThe pattern generalizes well beyond deepagents:\n\nThe full kit is on GitHub at [ajeetraina/docker-sbx-deepagents](https://github.com/ajeetraina/docker-sbx-deepagents) and on Docker Hub at `ajeetraina777/sbx-kit-deepagents`. It is small enough to read in one sitting and a good starting point for packaging your own agent tooling the same way.", "url": "https://wpnews.pro/news/running-deepagents-in-a-docker-sandbox-with-no-cloud-keys", "canonical_source": "https://dev.to/ajeetraina/running-deepagents-in-a-docker-sandbox-with-no-cloud-keys-48il", "published_at": "2026-10-02 16:57:21+00:00", "updated_at": "2026-10-02 17:07:42.355146+00:00", "lang": "en", "topics": ["ai-agents", "ai-tools", "developer-tools", "ai-infrastructure", "mlops"], "entities": ["Docker", "DeepAgents", "LangChain", "LangGraph", "Docker Model Runner", "Docker Hub", "PyPI", "Anthropic"], "also_reported_by": [], "alternates": {"html": "https://wpnews.pro/news/running-deepagents-in-a-docker-sandbox-with-no-cloud-keys", "markdown": "https://wpnews.pro/news/running-deepagents-in-a-docker-sandbox-with-no-cloud-keys.md", "text": "https://wpnews.pro/news/running-deepagents-in-a-docker-sandbox-with-no-cloud-keys.txt", "jsonld": "https://wpnews.pro/news/running-deepagents-in-a-docker-sandbox-with-no-cloud-keys.jsonld"}}