PromptABCD
FeaturesLearnHow it worksUse casesFAQGuideBlogContext Blocks
Sign inGet started free
Sign inSign up
PromptABCD

A calm home for your best AI prompts. Save them once, find them in seconds, reuse them forever.

Product

  • Features
  • Chrome Extension
  • Free Courses
  • How it works
  • Use cases
  • Blog
  • Context Blocks
  • Export Anywhere
  • FAQ

Resources

  • User guide
  • Learn prompting
  • Sign in
  • Get started free

© 2026 PromptABCD. All rights reserved.

Privacy PolicyTerms and Conditions
Home/Blog/AI Harness/Isolating Untrusted Code With Containers
AI Harness

Isolating Untrusted Code With Containers

Running agent-written code in a bare subprocess is not a sandbox. This teardown rebuilds it with real agent harness container isolation using Docker flags that actually hold.

September 8, 2026·9 min read
ShareShare
⚡Featured Prompt— copy and use right now
def run_agent_code(code: str) -> str:
    with open("/tmp/agent_snippet.py", "w") as f:
        f.write(code)
    result = subprocess.run(
        ["python", "/tmp/agent_snippet.py"],
        capture_output=True, text=True, timeout=30,
    )
    return result.stdout + result.stderr

How do you let an AI agent run code it wrote itself without letting that code delete your database, read your secrets, or mine crypto on your dime? That's the question every team reaches eventually, usually right after the first time an agent runs something surprising. The answer is agent harness container isolation — putting a real operating-system boundary between the agent's code and everything you care about. This is a teardown: we start with the weak version almost everyone ships first, break down exactly why it fails, then rebuild it properly.

Running agent-generated code without isolation is like handing a stranger the keys to your house because they promised to only use the kitchen. Maybe they will. But "maybe" is not a security model, and the model writing your code was trained on the entire internet, injection payloads included.

Before: The Weak Prompt

Here's the isolation strategy most harnesses start with — a subprocess and a hope.

hljs python
[object Object], ,[object Object],(,[object Object],) -> ,[object Object],:
    ,[object Object], ,[object Object],(,[object Object],, ,[object Object],) ,[object Object], f:
        f.write(code)
    result = subprocess.run(
        [,[object Object],, ,[object Object],],
        capture_output=,[object Object],, text=,[object Object],, timeout=,[object Object],,
    )
    ,[object Object], result.stdout + result.stderr

What this does: Writes the agent's code to a temp file and runs it with the system Python, capturing output under a 30-second timeout. It feels contained because it's a separate process. It is not contained in any meaningful sense — that process runs as your user, sees your filesystem, and reaches your network.

Why It Fails

The subprocess shares everything with the parent. It runs as the same user, which means it can read every file that user can read — your SSH keys, your cloud credentials, your source code. It inherits the environment, so any secret in an environment variable is right there in

os.environ
. It has full network access, so it can reach your internal services and the open internet alike. And the timeout only limits wall-clock time; it does nothing about CPU, memory, or disk. A single line —
while True: x = x + "a" * 10**6
— exhausts your host's memory and takes the whole machine down with it.

I've seen this exact setup let agent-written code read a

~/.aws/credentials
file and spin up compute in someone else's account. Nobody wrote malicious code on purpose. The model produced a plausible-looking script that happened to walk the home directory, and there was nothing to stop it.

⚠️ Common mistake: Believing a subprocess is a sandbox. A subprocess is an isolation boundary for crashes, not for security. If the code inside it can read your files and reach your network, it isn't sandboxed — it's just running somewhere slightly to the left of your main program, with all the same power.

After: The Improved Prompt

Real isolation means the code runs in a container with its own filesystem, no host credentials, dropped Linux capabilities, and hard resource limits. Here's the rebuilt version using Docker.

hljs python
[object Object], docker, tempfile, os

client = docker.from_env()

,[object Object], ,[object Object],(,[object Object],) -> ,[object Object],:
    ,[object Object], tempfile.TemporaryDirectory() ,[object Object], workdir:
        ,[object Object], ,[object Object],(os.path.join(workdir, ,[object Object],), ,[object Object],) ,[object Object], f:
            f.write(code)
        container = client.containers.run(
            image=,[object Object],,
            command=[,[object Object],, ,[object Object],],
            volumes={workdir: {,[object Object],: ,[object Object],, ,[object Object],: ,[object Object],}},
            network_mode=,[object Object],,
            mem_limit=,[object Object],,
            nano_cpus=,[object Object],,          ,[object Object],
            pids_limit=,[object Object],,
            cap_drop=[,[object Object],],
            security_opt=[,[object Object],],
            read_only=,[object Object],,
            user=,[object Object],,             ,[object Object],
            detach=,[object Object],,
        )
        ,[object Object],:
            result = container.wait(timeout=,[object Object],)
            logs = container.logs().decode()
        ,[object Object],:
            container.remove(force=,[object Object],)
        ,[object Object], logs

What this does: Runs the agent's code in a throwaway container with no network (

network_mode="none"
), a read-only root filesystem, 256 MB of memory, half a CPU, at most 64 processes, all Linux capabilities dropped, no privilege escalation, and running as the unprivileged
nobody
user. The code sees a clean minimal filesystem with none of your secrets, can't reach the network, and physically cannot exhaust the host. Then the container is destroyed.

Breaking Down Each Element

Each flag closes a specific hole from the "before" version, so it's worth understanding what every one buys you.

network_mode="none"
removes the network entirely. This is the single most important line for agent harness container isolation — code with no network can't exfiltrate data, can't phone home, and can't attack your internal services, no matter what it does internally.

cap_drop=["ALL"]
and
no-new-privileges
strip the Linux capabilities that let a process do privileged things and prevent it from regaining them via setuid binaries. Combined with
user="65534:65534"
, the code runs as a user with essentially no rights.

hljs python
volumes={workdir: {,[object Object],: ,[object Object],, ,[object Object],: ,[object Object],}}

What this does: Mounts only the single temp directory containing the snippet, read-only. The container can read its own code and nothing else from your host. There is no path from inside the container to your home directory, because your home directory was never mounted.

mem_limit
,
nano_cpus
, and
pids_limit
turn resource exhaustion from an outage into a contained failure. The infinite-append loop that killed the host earlier now just gets OOM-killed inside its 256 MB box, and the container dies alone.

⚡ Pro tip: Set

read_only=True
on the root filesystem and mount a small
tmpfs
at
/tmp
if the code needs scratch space. Most agent-generated code writes nothing to disk, and the ones that try to drop a persistent payload can't. A writable
/tmp
with a size cap gives legitimate code room to work without giving malware a home.

Variations for Different Contexts

Not every environment can run Docker, and the isolation primitive changes with your platform.

For a data-science agent that needs numpy and pandas, bake those into a custom image instead of

python:3.12-slim
, so startup doesn't pay a
pip install
tax on every run. A prebuilt image also means the container has no package manager and no network to fetch from — the dependency set is frozen and auditable.

For serverless or Kubernetes environments where the Docker socket isn't available, gVisor (

runsc
) or Kata Containers give you stronger kernel isolation than plain containers, at a small performance cost. On managed platforms, per-execution microVMs like Firecracker are what the big code-execution providers actually use under the hood — each run gets its own tiny virtual machine.

For a quick local prototype where you just need some boundary today, even an unprivileged container with

--network none
and a memory limit is a massive improvement over a bare subprocess. Don't let "perfect isolation" stop you from shipping "good isolation."

⚡ Pro tip: Whatever primitive you pick, make the container ephemeral — one container per execution, destroyed immediately after. A long-lived sandbox that runs many snippets lets one run's leftovers (a written file, a poisoned cache) affect the next. Fresh container every time is both simpler to reason about and safer.

⚠️ Common mistake: Mounting the Docker socket into the container to let the agent "manage its own containers." Handing

/var/run/docker.sock
to sandboxed code is equivalent to giving it root on the host — it can start a new privileged container that mounts your entire filesystem. If an agent needs to orchestrate containers, put that logic in the trusted harness, never inside the sandbox.

Verifying the Isolation Actually Holds

Configuring the flags is one thing; proving they work is another, and the gap between the two is where breaches live. Don't trust that agent harness container isolation is working because the config looks right — test it with code that deliberately tries to break out, and confirm each attempt fails.

hljs python
PROBES = {
    ,[object Object],:  ,[object Object],,
    ,[object Object],:  ,[object Object],,
    ,[object Object],:,[object Object],,
    ,[object Object],: ,[object Object],,
}

,[object Object], ,[object Object],():
    ,[object Object], name, code ,[object Object], PROBES.items():
        logs = run_agent_code(code)
        ,[object Object], ,[object Object], ,[object Object], logs ,[object Object], logs == ,[object Object],, ,[object Object],
        ,[object Object],(,[object Object],)

What this does: Runs a battery of escape attempts — reaching the network, reading host files, forking without limit, dumping environment variables — and asserts each one fails inside the container. If any probe succeeds where it should fail, your isolation has a hole and the test tells you exactly which one. Run this in CI so a config regression can't ship silently.

⚡ Pro tip: Keep these probes in your test suite and run them on every change to your container config. Isolation settings are exactly the kind of thing a well-meaning "just add one mount" pull request quietly weakens. A failing probe in CI catches the regression before it reaches production, where you'd otherwise learn about it from an incident instead of a red build.

The probes double as documentation. A new engineer reading

test_isolation
learns your entire threat model in thirty seconds — these are the attacks we care about, and here's proof we stop them. That's far more convincing than a comment saying "this is sandboxed."

Real-World Fit

A code-education platform runs student-submitted and AI-generated solutions side by side in identical

network_mode="none"
containers, which means the same isolation that stops a malicious student also contains a hallucinated
rm -rf
. A fintech analytics team runs agent-generated SQL-to-Python transforms in per-run containers with no credentials, passing data in through a read-only mount and collecting results from stdout, so the transform code never touches the warehouse directly. A security-research group uses gVisor-backed containers specifically because they run code they expect to be hostile, and the extra kernel boundary is worth the latency.

Save and Reuse This

One more thing before you ship it: measure the startup cost. A cold container adds real latency per execution — often a couple hundred milliseconds to a second — and for an agent that runs many small snippets, that adds up fast. If it matters for your workload, keep a small warm pool of pre-started containers and hand snippets to whichever one is free, resetting it between uses. You keep the isolation and lose most of the cold-start tax. Just don't reuse a container that ran something suspicious — destroy those and start fresh.

The container-run wrapper above is the kind of thing you write once and reuse across every agent you build — the flags are fiddly, the failure modes are subtle, and you do not want to reconstruct them from memory under deadline. Keep your hardened

run_agent_code
and its full flag set in a shared library like PromptABCD alongside the prompts that drive the agent, so the isolation config that took real thought to get right travels with the code that depends on it — and nobody ships a new service with the "before" version by accident.

ai-harnesscontainer-isolationdockersandboxingsecuritycode-execution

Continue Reading

Deterministic Replay for Harness Debugging
AI Harness

Deterministic Replay for Harness Debugging

An agent bug you can't reproduce is a bug you can't fix. Harness deterministic replay records every non-deterministic result and replays a run byte-for-byte on demand.

September 9, 2026·9 min read
How to Benchmark Two Harnesses Head-to-Head
AI Harness

How to Benchmark Two Harnesses Head-to-Head

Success rate alone hides cost and picks wrong. A proper agent harness benchmark runs many trials, measures cost per success, and isolates the harness from the model.

September 9, 2026·8 min read
Instrumenting a Harness With OpenTelemetry
AI Harness

Instrumenting a Harness With OpenTelemetry

An agent run is already shaped like a trace. Agent harness opentelemetry turns an undebuggable multi-service run into a span-by-span tree you can inspect in minutes.

September 9, 2026·8 min read

Save the prompts from this post

PromptABCD is a free prompt manager. Paste, organize, and reuse your best AI prompts — no more hunting through chat history.

Start free →
← PreviousSecurity Hardening for an AI Agent HarnessNext →Limiting Network Access in Your Harness
Share this post:
ShareShare