{"slug": "running-ai-agents-with-customized-templates-using-docker-sandbox", "title": "Running AI agents with customized templates using docker sandbox", "summary": "The article explains how to create custom Docker sandbox templates for running AI agents safely in isolated microVMs, building on a previous post about using the Docker sandbox tool (sbx). It provides step-by-step instructions for modifying default templates—such as adding tools like .NET SDK—by switching between root and agent users for system-level and user-level installations. The author also includes a caveat about potential compatibility issues, noting that .NET builds may hang indefinitely in sandboxes depending on the project.", "body_md": "This post follows on directly from [my previous post](/running-ai-agents-safely-in-a-microvm-using-docker-sandbox/), in which I describe how to run AI agents safely using the docker sandbox tool, `sbx`\n\n. In this post I describe how to create custom templates, so that your sandboxes start with additional tools. I show both how to add tools to the default template, and how to start with a different docker image and layer-on the docker sandbox tooling later.\n\nAn initial caveat to this post: I've been somewhat struggling to get my personal projects working in docker sandboxes, with .NET just hanging indefinitely during builds. This seems to be specific to my projects, as a hello world build is fine, but a word of caution: your mileage may vary.\n\n[Running agents safely in a docker sandbox](#running-agents-safely-in-a-docker-sandbox)\n\nAs I described in [my previous post](/running-ai-agents-safely-in-a-microvm-using-docker-sandbox/), working with AI agents in their default mode can mean an infuriating number of tool calls, that interrupt your flow, and generally slow you down:\n\nHowever, ignoring these tool calls using the [\"bypass permissions\" mode](https://code.claude.com/docs/en/permission-modes#switch-permission-modes) (AKA YOLO/dangerous mode) can be, well, *dangerous*. There's plenty of examples of AI agents going rogue; do you want to risk it? [Docker Sandboxes](https://docs.docker.com/ai/sandboxes/) provide one solution.\n\nDocker sandboxes run in microVMs, which are isolated from the host machine. The only folder the sandbox can access is the working directory you give access to, and all network traffic goes through a network proxy, which can either block traffic, or it can inject credentials such that the coding agent never sees them directly.\n\nI've only used docker sandboxes for a short while, but I've found they work relatively well for my purposes. However, one limitation is that [some of the projects](https://github.com/datadog/dd-trace-dotnet) I'm working on have a bunch of requirements for tooling, which always needs to be installed in the sandbox. Doing that every time is a bit of a pain. Luckily, there's a solution: custom templates.\n\n[Creating a custom Claude Code template](#creating-a-custom-claude-code-template)\n\n[The Docker sandbox documentation](https://docs.docker.com/ai/sandboxes/agents/custom-environments/) describes how to create a custom template, based on one of the default templates. I'm going to use the Claud Code examples in this post, but there's different templates for each of [the supported agents](https://docs.docker.com/ai/sandboxes/agents/custom-environments/). For each supported image there are also 2 variants: one that includes a Docker Engine, and one that doesn't. e.g.\n\n`claude-code`\n\n—includes a variety of dev tools.`claude-code-docker`\n\n—includes the same as above, but also has Docker Engine.\n\nThere's also a\n\n`claude-code-minimal`\n\ntemplate which is similar to`claude-code`\n\n, but includes fewer tools, so you don't have npm, python, or golang, for example.\n\nTo create a custom template, you need to have Docker Desktop installed as you're basically building an OCI image ([ effectively a docker image, kinda, sorta](https://github.com/opencontainers/image-spec)). That's despite the fact that docker sandboxes\n\n*don't*run as docker containers, but rather as microVMs.\n\nThe following example, [based on the documentation](https://docs.docker.com/ai/sandboxes/agents/custom-environments/#build-a-custom-template) shows how to start from the default template, how to install package manager dependencies, and how to install other tools, using `dotnet`\n\nas an example:\n\n```\nFROM docker/sandbox-templates:claude-code-docker\n\n# Switch to root to run package manager installs (.NET dependencies)\nUSER root\nRUN apt-get update \\\n    && apt-get install -y --no-install-recommends \\\n    ca-certificates \\\n    libc6 \\\n    libgcc-s1 \\\n    libgssapi-krb5-2 \\\n    libicu76 \\\n    libssl3t64 \\\n    libstdc++6 \\\n    tzdata \\\n    zlib1g\n\n# Most tools should be installed at user-level, using the agent user\nUSER agent\nRUN curl -sSL https://dot.net/v1/dotnet-install.sh | bash /dev/stdin --channel 10.0 --no-path\nENV DOTNET_ROOT=/home/agent/.dotnet \\\n    PATH=$PATH:/home/agent/.dotnet:/home/agent/.dotnet/tools\n```\n\nThis shows several important things:\n\n- The base\n`docker/sandbox-templates`\n\nimages are based on Ubuntu, so use`apt-get`\n\nfor managing packages. - The base images include two users,\n`root`\n\nand`agent`\n\n.- System-level package installations must be made using the\n`root`\n\nuser. - Tools that install into the home directory must be installed using the\n`agent`\n\nuser.\n\n- System-level package installations must be made using the\n\nYou can build the package using familiar `docker build`\n\ncommands, but you *must* push it straight to an OCI registry ([Docker Hub](https://hub.docker.com/) works!). You can't just build it locally as the docker sandbox doesn't share the image store with your local Docker host.\n\n```\ndocker build -t my-org/my-template:v1 --push .\n```\n\nOnce you've pushed the image to an OCI registry you can use it locally in a sandbox by using the `--template`\n\nor `-t`\n\nargument when calling `sbx run`\n\n:\n\n```\nsbx run -t docker.io/my-org/my-template:v1 claude\n```\n\nThis will pull (and cache) the template you specify, and you'll have the extra tools immediately available in your sandbox. Note that you must include the `docker.io`\n\n(Docker Hub) or other prefix when specifying the template (which differs from when you're running \"normal\" docker commands).\n\nI've created\n\n[some sandboxes for .NET], similar to the above, and pushed them to dockerhub. You can see[the definition of the images]here. Feel free to use them if you wish!\n\nBasing your custom templates on the standard default templates works well when you just want to make some extra tools available to your sandbox, but what if you fundamentally want to use a different base image? That's a bit trickier…\n\n[What if you need to change the base image?](#what-if-you-need-to-change-the-base-image-)\n\nThe \"supported\" approach to these custom templates is shown in the previous section: you start with the `docker/sandbox-templates`\n\nand then install the extra tools on that base image. Currently, those images are based on ubuntu 25.10, which is a nice current base image. But what if you *need* to use an older image for running tests. This is the case for the [Datadog .NET SDK](https://github.com/DataDog/dd-trace-dotnet) where we build using old distro versions to ensure we can support customers running with early glibc versions.\n\nThis proves a little tricky, as it's not officially supported. On the one hand, to emulate the work the base images do, *mostly* there's just a few crucial configurations you need to add, such as setting `NO_PROXY`\n\n, creating an `agent`\n\nuser, and installing the `claude`\n\nCLI. However, the `docker/sandbox-templates`\n\nimages contain a lot more than that. Unfortunately, the contents of these images aren't readily available on GitHub, for example.\n\nLuckily, you *can* see the contents of each layer [on Docker Hub](https://hub.docker.com/layers/docker/sandbox-templates/claude-code-minimal/images/sha256-39c0713656fb1f8531df04fbd6cf8d5a64e6002c5331e47ded1d7d3250ff2230). It's a little bit messed up due to how buildkit renders it, but it *is* understandable. Based on each of those layers, I was able to effectively reverse-engineer the layering the `docker/sandbox-templates:claude-code-docker`\n\nimage *on top* of a different base image.\n\nThis is all very hacky, could change at any time, and comes with no guarantees that it works for you 😅\n\nThe following shows a dockerfile that aims to perform all the steps the default `docker/sandbox-templates`\n\nto, but based on an arbitrary base image. There's quite a lot in here, but in summary:\n\n- It configures various environment variables.\n- Installs various basic tools (curl, certificates) and sets up various keyrings.\n- Configures the\n`agent`\n\nuser. - Sets up a\n`CLAUDE_ENV_FILE`\n\ntemporary session file. - Installs a variety of tools (npm, golang, python, make etc).\n- Installs Claude Code.\n\nAll in all, it looks a bit like this:\n\n```\nFROM dd-trace-dotnet/debian-tester AS base\n\n# Grab stuff from the original sandbox\nENV NPM_CONFIG_PREFIX=/usr/local/share/npm-global\nENV PATH=/home/agent/.local/bin:/usr/local/share/npm-global/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin\nENV NO_PROXY=localhost,127.0.0.1,::1,172.17.0.0/16\nENV no_proxy=localhost,127.0.0.1,::1,172.17.0.0/16\n\nWORKDIR /home/agent/workspace\nRUN apt-get update \\\n    && apt-get install -yy --no-install-recommends \\\n    ca-certificates \\\n    curl \\\n    gnupg \\\n    && install -m 0755 -d /etc/apt/keyrings \\\n    && curl -fsSL https://download.docker.com/linux/debian/gpg | \\\n    gpg --dearmor -o /etc/apt/keyrings/docker.gpg \\\n    && chmod a+r /etc/apt/keyrings/docker.gpg \\\n    && echo \\\n    \"deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/debian \\\n    $(. /etc/os-release && echo \"${UBUNTU_CODENAME:-$VERSION_CODENAME}\") stable\" | \\\n    tee /etc/apt/sources.list.d/docker.list > /dev/null\n\n# Remove base image user\n# Create non-root user\n# Configure sudoers\n# Create sandbox config\n# Set up npm global package folder under /usr/local/share\nRUN userdel ubuntu || true \\\n    && useradd --create-home --uid 1000 --shell /bin/bash agent \\\n    && groupadd -f docker \\\n    && usermod -aG sudo agent \\\n    && usermod -aG docker agent \\\n    && mkdir /etc/sudoers.d \\\n    && chmod 0755 /etc/sudoers.d \\\n    && echo \"agent ALL=(ALL) NOPASSWD:ALL\" > /etc/sudoers.d/agent \\\n    && echo \"Defaults:%sudo env_keep += \\\"http_proxy https_proxy no_proxy HTTP_PROXY HTTPS_PROXY NO_PROXY SSL_CERT_FILE NODE_EXTRA_CA_CERTS REQUESTS_CA_BUNDLE JAVA_TOOL_OPTIONS\\\"\" > /etc/sudoers.d/proxyconfig \\\n    && mkdir -p /home/agent/.docker/sandbox/locks \\\n    && chown -R agent:agent /home/agent \\\n    && mkdir -p /usr/local/share/npm-global \\\n    && chown -R agent:agent /usr/local/share/npm-global\n\nRUN touch /etc/sandbox-persistent.sh && chmod 644 /etc/sandbox-persistent.sh && chown agent:agent /etc/sandbox-persistent.sh\nENV BASH_ENV=/etc/sandbox-persistent.sh\n\n# Source the sandbox persistent environment file\n# Export BASH_ENV so non-interactive child shells also source the persistent env\nRUN echo 'if [ -f /etc/sandbox-persistent.sh ]; then . /etc/sandbox-persistent.sh; fi; export BASH_ENV=/etc/sandbox-persistent.sh' \\\n    | tee /etc/profile.d/sandbox-persistent.sh /tmp/sandbox-bashrc-prepend /home/agent/.bashrc > /dev/null \\\n    && chmod 644 /etc/profile.d/sandbox-persistent.sh \\\n    && cat /tmp/sandbox-bashrc-prepend /etc/bash.bashrc > /tmp/new-bashrc \\\n    && mv /tmp/new-bashrc /etc/bash.bashrc \\\n    && chmod 644 /etc/bash.bashrc \\\n    && rm /tmp/sandbox-bashrc-prepend\n    && chmod 644 /home/agent/.bashrc \\\n    && chown agent:agent /home/agent/.bashrc\n\nUSER root\n\n# Setup Github keys\nRUN curl -fsSL https://cli.github.com/packages/githubcli-archive-keyring.gpg \\\n    | tee /etc/apt/keyrings/githubcli-archive-keyring.gpg > /dev/null \\\n    && chmod a+r /etc/apt/keyrings/githubcli-archive-keyring.gpg \\\n    && echo \"deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/githubcli-archive-keyring.gpg] https://cli.github.com/packages stable main\" \\\n    | tee /etc/apt/sources.list.d/github-cli.list > /dev/null\n\n# Install all the tools available in the claude-code-docker image\nRUN apt-get update \\\n    && apt-get install -yy --no-install-recommends \\\n    dnsutils \\\n    docker-buildx-plugin \\\n    docker-ce-cli \\\n    docker-compose-plugin \\\n    git \\\n    jq \\\n    less \\\n    lsof \\\n    make \\\n    procps \\\n    psmisc \\\n    ripgrep \\\n    rsync \\\n    socat \\\n    sudo \\\n    unzip \\\n    gh \\\n    bc \\\n    default-jdk-headless \\\n    golang \\\n    man-db \\\n    nodejs \\\n    npm \\\n    python3 \\\n    python3-pip \\\n    containerd.io docker-ce \\\n    && apt-get clean \\\n    && rm -rf /var/lib/apt/lists/*\n\nLABEL com.docker.sandboxes.start-docker=true\n\nUSER agent\n\nFROM base AS claude\n\n# Install Claude Code\nRUN curl -fsSL https://claude.ai/install.sh | bash\n\nENV CLAUDE_ENV_FILE=/etc/sandbox-persistent.sh\nCMD [\"claude\", \"--dangerously-skip-permissions\"]\n```\n\nIf you don't want *all* the extra tools like npm, python and golang, you can instead base it on the `claude-code-minimal`\n\nimage instead. In that case, the final tool install step looks a bit like this instead:\n\n```\nRUN apt-get update \\\n    && apt-get install -yy --no-install-recommends \\\n        bubblewrap \\\n        dnsutils \\\n        docker-buildx-plugin \\\n        docker-ce-cli \\\n        docker-compose-plugin \\\n        git \\\n        gh \\\n        jq \\\n        less \\\n        lsof \\\n        make \\\n        procps \\\n        psmisc \\\n        ripgrep \\\n        rsync \\\n        socat \\\n        sudo \\\n        unzip \\\n    && apt-get clean \\\n    && rm -rf /var/lib/apt/lists/*\n```\n\nOr, you know, install a mix of those tools. That's the advantage of this approach at least, you can install more of fewer tools, whatever you want! Whichever approach you like, you can again build and push the image to an OCI registry:\n\n```\ndocker build --tag dd-trace-dotnet/sandbox --push .\n```\n\nYou can then use the image in your `sbx`\n\nsandbox, just as before, but this time you'll be running in a base image that has all of your prerequisites installed.\n\n[Updating the version of Claude Code only](#updating-the-version-of-claude-code-only)\n\nYou might notice in the above Dockerfile that I put the Claude Code image in its own section of the multi-stage build:\n\n```\nFROM base AS claude\n\n# Install Claude Code\nRUN curl -fsSL https://claude.ai/install.sh | bash\n\nENV CLAUDE_ENV_FILE=/etc/sandbox-persistent.sh\nCMD [\"claude\", \"--dangerously-skip-permissions\"]\n```\n\nThat's not necessary, but I did it for a subtle reason. Claude Code updates a *lot*, but I didn't really want to update the *entire* image repeatedly for performance reasons. By moving the Claude Code install to its own final stage, I could rebuild *just* that stage, without having to rebuild the *entire* image, by using `--no-cache-filter`\n\n:\n\n```\ndocker build --tag dd-trace-dotnet/sandbox --push --no-cache-filter claude .\n```\n\nIt's just a minor thing, but it means updating to the latest Claude Code version is a much quicker process.\n\nI still need to test this image out properly, but I tried it out with a previous version and it was working pretty well for me. I'd be interested to know if anyone else has tried something similar, or if you have a better solution (short of just yolo/dangerous direct on the host!).\n\n[Summary](#summary)\n\nIn this post I described how to create custom templates for Docker Sandboxes. First I showed the official approach, which layers tools on top of yhe default sandbox templates in `docker/sandbox-templates`\n\n. This is the easiest approach, and works well if the specific base image doesn't matter too much to you. Then I showed how I reverse-engineered the sandbox templates to allow completely swapping out the base image. This was necessary for a project I was working on, where I specifically wanted to run agents in the same base image we use to build the project. this approach isn't supported, and I'm not 100% it's quite right, but it seems to do the job well enough!", "url": "https://wpnews.pro/news/running-ai-agents-with-customized-templates-using-docker-sandbox", "canonical_source": "https://andrewlock.net/running-ai-agents-with-customized-templates-in-docker-sandbox/", "published_at": "2026-04-14 10:00:00+00:00", "updated_at": "2026-05-23 21:36:53.164474+00:00", "lang": "en", "topics": ["artificial-intelligence", "developer-tools", "cloud-computing", "open-source"], "entities": ["Docker", "sbx"], "alternates": {"html": "https://wpnews.pro/news/running-ai-agents-with-customized-templates-using-docker-sandbox", "markdown": "https://wpnews.pro/news/running-ai-agents-with-customized-templates-using-docker-sandbox.md", "text": "https://wpnews.pro/news/running-ai-agents-with-customized-templates-using-docker-sandbox.txt", "jsonld": "https://wpnews.pro/news/running-ai-agents-with-customized-templates-using-docker-sandbox.jsonld"}}