An AI coding agent should run inside a boundary that assumes the agent can make mistakes and that repository content may be hostile. Give it one disposable workspace, no ambient credentials, no network by default, a non-root identity, and explicit CPU, memory, process, and time limits. Keep approval outside that boundary for operations with consequences. A container can be a useful implementation, but a container alone is not the security policy.
This article focuses on a local or self-managed agent that can read files, edit code, and run commands. Product-specific controls change, so the factual product and runtime details below were checked against official documentation on 2026-07-23.
1. Define the agent threat model
Start with capabilities, not with a preferred product. A coding agent combines model-generated commands, repository instructions, package scripts, build tools, and external tools. Any input can be wrong or malicious.
Map at least five assets:
- source code and unreleased product details;
- credentials, signing keys, cloud metadata, and authenticated developer sessions;
- files outside the assigned repository and services reachable over the network;
- the integrity and availability of the host running the agent.
List the actions the agent can request—reading, writing, deleting, executing, installing, networking, and invoking tools—and record their worst plausible consequences.
Use three practical risk levels:
- Routine inside the boundary: reading assigned source, writing a disposable worktree, and running pinned local tests.
- Boundary crossing: downloading a dependency, reading another directory, opening a browser session, or invoking a tool with external access.
- Consequential: publishing, deploying, sending a message, changing access, handling production data, spending money, or destroying durable state.
The sandbox should automatically permit only the first class. The second class should be narrowly granted for a specific purpose. The third class should stop for independent review and usually run through a separate system identity.
The following table turns that model into a minimum control set. Values are starting points, not guarantees; measure the repository’s normal build before tightening them.
| Control | Safer default | Grant only when | Evidence to retain |
|---|---|---|---|
| Writable files | One disposable task worktree | The task requires an additional exact path | Final changed-file list and diff |
| Outbound network | Disabled | A named destination is required for the current step | Destination, decision, and timestamp |
| Credentials | None | A short-lived, task-scoped identity is unavoidable | Scope and expiry, never the secret |
| User and privileges | Non-root, no privilege gain | A reviewed build requirement cannot run otherwise | Exact exception and rollback |
| Memory / CPU / processes | Explicit measured limits | A known test exceeds the baseline | Limit-hit event and revised measurement |
| Consequential actions | Separate approval and executor | A human approves the exact final action | Bound action, target, approver, and expiry |
Prompt injection is not limited to chat. A README, issue, code comment, test output, package metadata field, or web page can tell the model to reveal secrets or broaden its task. Treat repository and network content as untrusted data. OpenAI describes the sandbox as the technical boundary for writable paths, network access, and protected paths, while approval controls requests outside it. Anthropic documents a similar combination of isolation and permission.
2. Isolate filesystem access
Create a fresh workspace for each task. A temporary clone or worktree limits accidental cross-task edits and makes the final diff easier to inspect. Do not launch the agent from a home directory or a parent folder containing multiple repositories. For the Git mechanics and ownership rules, see How to Use Git Worktrees with Multiple AI Coding Agents.
Build the filesystem policy from deny-by-default rules:
- mount the assigned source read-only when the task is analysis-only;
- for editing tasks, expose one writable worktree rather than the original checkout;
- keep
.git, agent configuration, host sockets, SSH directories, cloud configuration, password stores, and other repositories outside the writable set; - provide dedicated scratch and cache directories;
- resolve real paths so
.., symlinks, junctions, and alternate spellings cannot escape the root; - reject special files and unexpected device mounts.
Mount only the required package cache, preferably read-only. Do not solve a missing dependency by mounting the entire user profile. Expose task-specific copies of input data, not production records.
Docker supports a read-only root filesystem with selected writable volumes or temporary filesystems. Its documentation warns that mounting the Docker socket gives broad control over the host daemon. Do not mount /var/run/docker.sock for ordinary agent work.
Run the agent as an unprivileged user. On Linux, rootless Docker runs both the daemon and containers without root privileges inside a user namespace. Rootless mode reduces the impact of runtime flaws, but it does not make exposed files, credentials, or networks safe by itself.
A container shares the host kernel. NIST’s container security guidance treats container deployments as a distinct security problem involving access control, configuration, communications protection, and system integrity; a container image is therefore only one layer of the design. For an unknown repository, adversarial package scripts, or a high-value workstation, prefer a short-lived virtual machine or hardened runtime.
GitHub documents that each Codespace uses a separate newly built VM and isolated network, illustrating the stronger isolation of per-session VMs. Another option is a sandboxed runtime such as gVisor, whose Sentry intercepts workload system calls and implements the Linux system-call interface in userspace, reducing direct exposure to the host kernel. gVisor also documents compatibility, performance, higher-layer, and hardware-side-channel limits, so test the actual toolchain rather than treating it as a drop-in guarantee.
For safer generated-code review practices after the agent edits the isolated workspace, see How to Review AI-Generated Code.
3. Restrict network and credentials
Start with outbound network access disabled. This blocks many exfiltration and supply-chain paths while allowing local tests. OpenAI says its internal Codex deployment avoids open-ended outbound access: expected destinations are allowed, unwanted destinations blocked, and unfamiliar domains reviewed.
When network access is necessary, grant destinations rather than “the internet.” Separate phases:
- A trusted setup phase fetches pinned dependencies and prepares the image.
- The agent phase runs with no network.
- A controlled research or dependency phase uses an allowlist, proxy, and logs.
An allowlist should cover hostname, protocol, port, and redirects. Block loopback, link-local and private addresses, cloud metadata, local control planes, and DNS rebinding paths unless required. Do not publish ports by default.
Do not inject a developer’s normal environment. Allowlist variables and remove API keys, GITHUB_TOKEN, cloud profiles, SSH agents, credential helpers, cookies, signing material, and production URLs. Absence is safer than redaction.
When authentication is unavoidable, use a short-lived credential scoped to that task and destination. Keep the real credential behind a trusted proxy when possible. Anthropic documents this proxy pattern for cloud execution, while OpenAI documents storing CLI and MCP OAuth credentials in the OS keyring.
Remember that isolation products have different defaults. GitHub Codespaces documentation states that codespaces can make outbound internet connections by default, even though inbound connections and inter-codespace traffic are restricted. A remote VM is therefore not automatically an egress-restricted sandbox.
4. Separate approval from execution
An approval prompt presented by the agent that selected the command is not an independent boundary. The agent may misunderstand the target, omit a side effect, or relay an injected instruction.
Put the approval controller outside the sandbox and bind approval to a concrete action:
- normalized command and arguments;
- exact files, repository, account, and environment;
- destination host and data leaving the boundary;
- credential or permission class, without exposing the secret;
- expected side effects and rollback;
- expiry time and single-use identifier.
Re-approve if any material field changes. Silence, a timeout, previous approval for a similar command, text found in a repository, and model-generated “approved” output are not consent.
Use a small policy matrix. Reads within the task root and writes inside the disposable workspace can run automatically after path enforcement. Dependency installation, extra directories, and new network destinations need a scoped grant. Deployment, publication, destructive changes, access control, secrets, and financial actions need a human decision and separate executor.
Avoid blanket “always allow” rules for shells or package managers. Arguments, configuration, hooks, and environment variables can change behavior. Anthropic notes that sensitive operations need approval and unmatched commands fail closed; OpenAI describes rules that block or review dangerous command patterns.
Machine-checkable gates are more reliable than prose alone. The approach in Machine Gates for AI Output can be applied to path boundaries, allowed destinations, changed-file lists, test evidence, and release conditions.
5. Limit processes, time, and resources
A safe agent should not exhaust the host. Recursive builds, runaway tests, huge generated files, and log floods are ordinary failures even without an attacker.
Set independent limits for:
- wall-clock time per command and for the entire task;
- CPU quota;
- memory and swap;
- process count;
- open files;
- writable disk and log volume;
- output captured by the orchestrator;
- retries and agent turns.
Docker exposes memory, CPU, process, user, read-only filesystem, and security options through docker run. Its official reference notes that memory and CPU are unlimited unless configured, and --pids-limit -1 means unlimited processes. Choose limits from measurements of the actual build rather than copying arbitrary numbers from an example.
A Linux-oriented starting shape might be:
docker run --rm \
--network none \
--read-only \
--tmpfs /tmp:rw,noexec,nosuid,size=256m \
--mount type=bind,src=/srv/task-worktree,dst=/workspace \
--workdir /workspace \
--user 10001:10001 \
--cap-drop ALL \
--security-opt no-new-privileges=true \
--memory 4g \
--cpus 2 \
--pids-limit 256 \
agent-image@sha256:<pinned-digest>
This is a template, not a universal secure command. Confirm writable-mount ownership, add only proven capabilities, set a host-side timeout, limit disk, and preserve default confinement. Docker’s default seccomp profile is an allowlist, and its documentation recommends against disabling it. Do not use --privileged or seccomp=unconfined for convenience.
Budgets should also exist above the operating-system layer. Limit tool calls, retries, and generated output so a looping agent stops predictably. Token Budgets and Circuit Breakers for AI Agents covers that complementary control.
On Linux, container resource settings are commonly enforced through cgroups. The kernel’s cgroup v2 documentation defines hierarchical controllers and interfaces such as memory.max and pids.max; it also notes that some defaults are effectively unlimited. Verify the host’s active cgroup version and confirm that limits are enforced with denial tests, not just present in a command line.
6. Test escape and prompt-injection scenarios
Do not declare the sandbox effective because a normal build succeeds. Test the actions the policy forbids after changes to the image, runtime, mounts, agent, OS, or approval rules.
Test at least these cases:
- read and write a sibling repository, parent directory, host configuration file, and protected VCS metadata;
- traverse through
.., a symlink, junction, bind mount, archive extraction, and alternate path spelling; - inspect environment variables, process arguments, credential stores, mounted sockets, and instance metadata;
- connect to a public host, loopback, private address, DNS alias, redirect target, and unexpected port;
- spawn processes until the process limit is reached;
- allocate memory, fill writable storage, flood logs, and exceed command time;
- request privilege escalation, add a capability, access a device, or disable the security profile;
- change an approved command after approval;
- place hostile instructions in a README, source comment, test output, dependency metadata, and fetched page.
Use harmless canaries instead of secrets. An escape attempt should produce a policy denial, not merely a model refusal. Record the normalized target, decision, approval reference, result, changed files, and network decision without raw secrets or complete private prompts.
Finally, test recovery: terminate the sandbox, revoke its scoped credential, discard its workspace, recreate a clean environment, and identify every external side effect. If those steps are unclear, the rollout is not ready.
If the agent can call external tools through MCP, apply the same boundary to each server and tool identity. MCP Security Checklist: Permissions, Secrets, and Tool Boundaries covers that layer in more detail.
7. Use a safe rollout checklist
Begin with low-value repositories and read-only tasks. Move to disposable writes only after the denial tests pass. Add controlled network access last, one destination and one workflow at a time.
Before enabling an agent:
- The threat model identifies files, credentials, networks, host resources, and consequential actions.
- One fresh task workspace is the only writable project path.
- Parent directories, other repositories, VCS internals, host sockets, devices, and credential stores are excluded.
- The process uses a non-root identity and cannot gain new privileges.
- The runtime’s default security profile remains enabled.
- Outbound network access is off or restricted through a logged allowlist.
- No ambient developer or production credentials enter the environment.
- Scoped credentials expire and can be revoked independently.
- Approval runs outside the sandbox and is bound to final targets and arguments.
- Deployment, publication, destructive changes, access changes, and spending use a separate executor.
- CPU, memory, process, disk, output, retry, and wall-clock limits are enforced.
- Escape, prompt-injection, changed-argument, and resource-exhaustion tests fail safely.
- Every run produces a reviewable diff and a concise action log.
- Operators can terminate the run, revoke access, discard the workspace, and investigate side effects.
The goal is not to prove an agent will never choose badly. It is to make unsafe choices ineffective by default, reviewable when necessary, and recoverable when a control fails.
Frequently asked questions
Is Docker alone enough to sandbox an AI coding agent?
No. A container can restrict files, privileges, network access, and resources, but the result depends on its mounts, runtime flags, host kernel, credentials, and approval system. Keep the root filesystem read-only where practical, avoid host sockets and ambient credentials, retain the default security profile, and test prohibited actions.
Should an AI coding agent have internet access?
Start with no outbound access. If a task needs a registry, documentation site, or API, allow only the required destination and phase, log the decision, and keep private, loopback, link-local, and metadata addresses blocked unless they are explicitly required.
Is a virtual machine safer than a container for coding agents?
A per-task VM generally provides a stronger kernel boundary, but configuration still matters. Use it for unknown or adversarial code and high-value hosts when its additional startup and resource cost is justified. Hardened runtimes can provide an intermediate option, subject to compatibility testing.
What resource limits should I set?
There is no universal safe number. Measure a clean build and the heaviest legitimate test, add a documented margin, then set independent CPU, memory, process, disk, output, retry, command-time, and task-time limits. Confirm each limit with a harmless exhaustion test.
How should approvals work for deployment or deletion?
Keep approval and execution outside the sandbox. Bind approval to the normalized command, exact target, identity, side effects, and expiry; require a new decision when any material field changes. Use a separate executor for deployment, publication, access changes, destructive actions, secrets, or spending.
Official primary sources
Checked on 2026-07-24:
- Running Codex safely at OpenAI: sandbox boundaries, approval policy, network allowlists, credential storage, managed rules, and audit trails.
- Claude Code security documentation: permission defaults, working-directory boundaries, sandboxing, prompt-injection safeguards, cloud isolation, and scoped credentials.
- Docker container run reference: read-only filesystems, mounts, users, capabilities, resource limits, process limits, and
no-new-privileges. - Docker seccomp security profiles: the default allowlist profile and guidance against disabling it.
- Docker Rootless mode: non-root daemon and container execution through a user namespace.
- Security in GitHub Codespaces: per-codespace VM and network isolation, outbound connectivity, token scope, and port-forwarding behavior.
- NIST Guidance on Application Container Security: container security challenges and the access-control, configuration, communications, and integrity control families.
- Linux kernel cgroup v2 documentation: hierarchical resource controllers and the memory, CPU, process, and I/O control interfaces.
- Introduction to gVisor security: userspace system-call interception, host-kernel exposure, supported platforms, and documented limitations.