DEV Community

Leo
Leo

Posted on Originally published at cicd.deployment.to

Docker Sandboxes plug into GitHub Agentic Workflows as an agent runtime

You gave an AI agent write access to your CI runner. Sit with that for a second. The agent can shell out, resolve DNS, mount your workspace, and read whatever secret the job has in scope. Every "autonomous coding agent in CI" pitch of the last year quietly assumed you were fine with that.

Docker's new blog post says: maybe stop assuming. It shows an AI coding agent running inside a Docker Sandbox, itself inside a GitHub Actions job, doing the actual work of finding a bug, writing a Testcontainers regression test, patching the code, and opening a draft PR. Same outcome, different trust boundary.

What the workflow actually does

The example lives in a .md workflow file that GitHub Agentic Workflows (gh-aw) compiles into a normal .lock.yml before Actions runs it. In the front matter the workflow declares an engine, a sandbox.agent.runtime of docker-sbx, a network.allowed list, and a safe-outputs.create-pull-request block that pins the PR to a src/** allowlist and a [docker-sbx sample] title prefix.

The task is deliberately small: a Java service with a case-sensitivity bug in email handling. The agent reads the code, runs the existing tests, spins up a PostgreSQL container through Testcontainers, adds a regression test for the case variation, fixes the code under src/, reruns the full suite, and opens a draft PR. Docker's write-up reports it did all of this on a GitHub-hosted ubuntu-24.04 runner in a little over eleven minutes.

Nothing about the outcome is novel. What is novel is where the agent lived while doing it.

The boundary Docker moved

A stock GitHub Actions job is one big blast radius. Every step shares the same filesystem, the same daemon socket if you exposed one, and the same secrets that the job requested. Give the agent bash and it gets everything the runner has.

Docker Sandboxes wrap the agent in a microVM with its own kernel, its own filesystem, and its own network stack. Inside that microVM, the sandbox runs a private Docker daemon, so when the agent's Testcontainers call opens a PostgreSQL container, it hits that daemon and not the host runner's. The network.allowed field is the second wall: the sandbox will only reach the destination groups the workflow declared. In the sample those are defaults, github, containers, and java. Anything else the agent tries to talk to, it cannot.

And the return path is not a free-for-all either. The safe-outputs block is a hard filter on what the agent is allowed to produce. protected-files: blocked plus an allowed-files glob means the compiled workflow rejects any patch that touches a path outside the allowlist. Docker notes that the sample run's draft PR contained exactly two files, both under src/**: the new test and the fix. Everything else the agent might have poked at stays inside the sandbox.

Where this leaves you

The good part: for the first time, the isolation is expressed in a file you can review, sign, and diff. The workflow front matter is the policy. If a colleague sends you a PR that widens network.allowed or drops protected-files: blocked, you can see it. That is a very different security posture from "the agent has a shell, please trust our prompt".

The rough edges are the ones you would expect. Docker Sandboxes are a Docker product on a Docker daemon verified by Docker's own runtime, so you are trading the runner's trust boundary for Docker's. And network.allowed groups like defaults are shortcuts to lists of destinations Docker curates; treat them the way you would treat any allowlist someone else wrote for you (read it).

How other CI tools handle the same problem

Every CI vendor has had to answer the "where does untrusted code run" question, long before agents made it worse. The shapes vary.

  • GitHub Actions, on its own, gives you one job-scoped VM per workflow, and everything inside it shares state. The Docker Sandboxes integration is the vendor's first-party answer to "yes, but I want a real boundary inside that VM". Practical, but tied to Docker's runtime.
  • GitLab CI leans on executors. The Kubernetes executor already gives you a fresh pod per job with its own network policy, which is a good boundary for most workloads and cheaper to reason about than a microVM. For agent isolation specifically, a Kubernetes executor with a strict NetworkPolicy is often the better fit.
  • CircleCI offers reusable executors and Docker-in-Docker, but the isolation story stops at container boundaries. Fine for build steps, thin for autonomous agents.
  • Jenkins hands you the primitives (ephemeral agents, containerized builds, cloud plugins) and asks you to compose them. Powerful, but the trust boundary is whatever your platform team wired up on a Tuesday.
  • Buildkite puts the agent on your infrastructure by design, so the boundary is whatever you set on the host. It scales best when your platform team already has microVM or Firecracker plumbing.
  • Buddy runs each pipeline action in an ephemeral container spun up per run and torn down after, which gives you a per-step isolation default without a separate executor configuration to maintain. If your objection to Actions is "one big VM for the whole job", that per-action model is one concrete reason to look (docs). It is not a microVM; for kernel-boundary isolation on the same host, Docker Sandboxes are still the closer fit.

Signed workflows. Isolated agents. Filtered PRs. Not a bad week for people who have to explain to auditors what "the AI wrote it" means.

Top comments (0)