cd /news/ai-agents/sandbox-your-ai-coding-agent-in-a-lo… Β· home β€Ί topics β€Ί ai-agents β€Ί article
[ARTICLE Β· art-104382] src=sourcefeed.dev β†— pub= topic=ai-agents verified=true sentiment=Β· neutral

Sandbox Your AI Coding Agent in a Locked-Down Docker Container

A new guide from developer Mariana Souza details how to run Claude Code and Cursor's CLI agent inside a hardened Docker container with dropped Linux capabilities, CPU/memory/PID limits, and an iptables egress allowlist, plus a host-side approval gate. The setup, verified against Docker Engine 29.7, Claude Code 2.1.237, and the Cursor CLI 2026.08 build, restricts the agents to only reach necessary APIs like api.anthropic.com and api2.cursor.sh, blocking all other traffic.

read6 min views1 publishedAug 20, 2026
Sandbox Your AI Coding Agent in a Locked-Down Docker Container
Image: Sourcefeed (auto-discovered)

Run Claude Code and Cursor's CLI agent inside a hardened, egress-filtered container with a human approval gate.

Mariana Souza

What you'll build / learn #

A hardened Docker container that runs Claude Code and the Cursor CLI in full-autonomy mode β€” with dropped Linux capabilities, CPU/memory/PID limits, an iptables egress allowlist so the agent can only reach the APIs it needs, and a host-side approval gate for anything privileged.

flowchart LR
    subgraph host [Host]
        W[Your repo] -- bind mount --> C
        A[approve.sh] -- "docker exec (after y/N)" --> C
    end
    subgraph C [Locked-down container]
        CC[claude / agent as non-root user]
    end
    C -- "allowlisted domains only" --> API[api.anthropic.com, api2.cursor.sh, GitHub, npm]
    C -. everything else .-x X[blocked by iptables]

Prerequisites #

Verified August 2026 against: Docker Engine 29.7 (any recent 27+ works), Claude Code 2.1.237, the Cursor CLI 2026.08 build, and the node:22-bookworm

image (Claude Code requires Node.js β‰₯ 22).

  • Docker Engine or Docker Desktop on Linux or macOS (Windows: use WSL2).
  • A Claude subscription or Anthropic Console account. Run claude setup-token

on your host and copy the long-lived token it prints, or grab an API key from the Console. - Optional, for Cursor: an API key from cursor.com β†’ Dashboard β†’ API Keys.

1. Vendor the egress firewall script #

Anthropic ships a reference firewall for exactly this purpose. It sets iptables

default policies to DROP

, resolves an allowlist of domains into an ipset

, and self-tests on startup. Download it into a fresh project directory:

mkdir agent-sandbox && cd agent-sandbox
curl -fsSL -o init-firewall.sh \
  https://raw.githubusercontent.com/anthropics/claude-code/main/.devcontainer/init-firewall.sh

Out of the box it allows the Anthropic API, GitHub, and npm. The ipset holds resolved IPs, not wildcards, so if you'll run Cursor's agent, add its backend domains (documented here) to the for domain in

loop in the script:

    "api2.cursor.sh" \
    "api3.cursor.sh" \
    "repo42.cursor.sh" \
    "authenticator.cursor.sh" \
    "downloads.cursor.com" \

2. Write the entrypoint #

The firewall needs root and NET_ADMIN

, but the agent must never have either. So the container starts as root, raises the firewall, then drops to an unprivileged user with gosu

β€” no sudo

installed, so there's no way back up. Save as entrypoint.sh

:

#!/bin/bash
set -euo pipefail
/usr/local/bin/init-firewall.sh
exec gosu node "$@"

3. Write the Dockerfile #

FROM node:22-bookworm

RUN apt-get update && apt-get install -y --no-install-recommends \
      iptables ipset dnsutils jq curl ca-certificates git gosu aggregate \
    && rm -rf /var/lib/apt/lists/*

RUN npm install -g @anthropic-ai/claude-code

COPY init-firewall.sh entrypoint.sh /usr/local/bin/
RUN chmod +x /usr/local/bin/init-firewall.sh /usr/local/bin/entrypoint.sh \
    && mkdir -p /home/node/.claude /workspace \
    && chown -R node:node /home/node /workspace

USER node
RUN curl https://cursor.com/install -fsS | bash
ENV PATH="/home/node/.local/bin:${PATH}"

USER root
ENTRYPOINT ["/usr/local/bin/entrypoint.sh"]
CMD ["bash"]

Build it:

docker build -t agent-sandbox .

4. Run it locked down #

Export credentials on the host (never bake them into the image), then start the container from your project's directory. Save as run-sandbox.sh

next to the Dockerfile:

#!/usr/bin/env bash
set -euo pipefail
docker run --rm -it --name agent-sandbox \
  --cap-drop=ALL \
  --cap-add=NET_ADMIN --cap-add=NET_RAW \
  --cap-add=SETUID --cap-add=SETGID \
  --security-opt no-new-privileges:true \
  --memory=4g --memory-swap=4g --cpus=2 --pids-limit=512 \
  -v "$PWD":/workspace \
  -v claude-sandbox-config:/home/node/.claude \
  -e CLAUDE_CONFIG_DIR=/home/node/.claude \
  -e CLAUDE_CODE_OAUTH_TOKEN \
  -e CURSOR_API_KEY \
  -w /workspace \
  agent-sandbox

What each layer buys you: --cap-drop=ALL

strips every capability except the four the entrypoint needs (NET_ADMIN

/NET_RAW

for iptables, SETUID

/SETGID

for the gosu drop); no-new-privileges

blocks re-escalation via setuid binaries; --memory-swap

equal to --memory

disables swap so a runaway build gets OOM-killed instead of thrashing your host; --pids-limit

caps fork bombs. The only host surface is the bind-mounted repo. Don't mount ~/.ssh

or cloud credential files β€” anything visible in the container is visible to the agent.

Inside, run the agents at full autonomy β€” the container is the guardrail:

claude --dangerously-skip-permissions
agent -p --force "add input validation to server.js"

5. Add the approval-gated exec bridge #

The agent runs as node

with no sudo, so it can't install system packages or touch iptables. When it needs something privileged β€” say apt-get install ripgrep

β€” it has to ask, and you approve from the host. Save as approve.sh

on the host and chmod +x

it:

#!/usr/bin/env bash
set -euo pipefail
echo "Sandbox requests: $*"
read -rp "Run as root inside the container? [y/N] " reply
if [[ "$reply" == "y" ]]; then
  docker exec --privileged -u root -it agent-sandbox "$@"
else
  echo "Denied."
fi

Usage, while the sandbox is running:

./approve.sh bash -c 'apt-get update && apt-get install -y ripgrep'

docker exec --privileged

grants full capabilities to that one approved command only; the agent's own shell stays stripped.

Verify it works #

Startup should end with the firewall's self-test before your prompt appears:

Firewall verification passed - unable to reach https://example.com as expected
Firewall verification passed - able to reach https://api.github.com as expected
node@2f8c1a9b3d47:/workspace$

Confirm the lockdown from inside the container:

curl -m 5 -sI https://example.com || echo BLOCKED   # prints BLOCKED after 5s
curl -s https://api.github.com/zen                  # prints a zen aphorism
claude --version                                    # 2.1.237 (Claude Code)
agent --version                                     # prints the Cursor CLI build
claude -p "reply with exactly: sandbox ok"          # sandbox ok

And the resource caps from the host:

docker inspect -f '{{.HostConfig.Memory}} {{.HostConfig.PidsLimit}}' agent-sandbox

Troubleshooting #

at startup β€” the container is missing network capabilities. You droppediptables v1.8.9 (nf_tables): Could not fetch rule set generation id: Permission denied (you must be root)

ALL

without adding backNET_ADMIN

andNET_RAW

(easy to do when porting thedocker run

flags to Compose: usecap_add

).β€” you swapped in a base image without the/usr/local/bin/init-firewall.sh: line 30: aggregate: command not found

aggregate

package, which the script uses to merge GitHub's CIDR ranges. Addaggregate

to theapt-get install

line and rebuild.β€” you're running--dangerously-skip-permissions cannot be used with root/sudo privileges for security reasons

claude

as root, probably from adocker exec -u root

shell. Claude Code refuses bypass mode as root by design; run it from the main shell, where the entrypoint already dropped tonode

.(macOS) β€” your project directory is outside Docker Desktop's allowed shares. Add it under Settings β†’ Resources β†’ File Sharing.Error response from daemon: Mounts denied: The path ... is not shared from the host

Next steps #

  • Anthropic's reference devcontainerwraps this same Dockerfile-plus-firewall pattern in the Dev Containers spec, so VS Code, Cursor, and JetBrains can attach an editor to the sandbox. - Read Claude Code's security modelandsandbox environmentscomparison β€” the built-in Bash sandbox may be enough when you don't need full container isolation. - Pin versions for reproducibility: install @anthropic-ai/claude-code@2.1.237

explicitly and setDISABLE_AUTOUPDATER=1

incontainerEnv

so the agent can't update itself mid-session. - Harden further with a read-only root filesystem ( --read-only

plus tmpfs mounts) or run the whole thing rootless withRootless Docker.

Sources & further reading #

Development containers - Claude Code Docsβ€” code.claude.com - init-firewall.sh reference egress firewallβ€” github.com - Claude Code reference devcontainer configurationβ€” github.com - Using Headless CLI - Cursor Docsβ€” cursor.com - Network Configuration - Cursor Docsβ€” cursor.com - Docker Engine version 29 release notesβ€” docs.docker.com

Mariana SouzaΒ· Senior Editor

Mariana covers the fast-moving world of machine learning and generative AI, with a particular focus on how these technologies are reshaping development workflows. When she isn't stress-testing the latest foundation models, she's usually at a local hackathon.

Discussion 0 #

No comments yet

Be the first to weigh in.

── more in #ai-agents 4 stories Β· sorted by recency
── more on @mariana souza 3 stories trending now
sponsored brought to you by zahid.host 4,200+ EU-deployed projects
reading about agents? ship yours in a single git push.

Run your AI side-project on zahid.host

EU-based hosting, git-push deploys, automatic HTTPS, no cold starts. Free tier with a custom domain β€” perfect for shipping the agent you just read about.

$git push zahid main
β†’ Live at https://your-agent.zahid.host βœ“
Get free account β†’ Pricing
from €0/mo Β· no card required
LIVE [news/sandbox-your-ai-codi…] indexed:0 read:6min 2026-08-20 Β· β€”