Run agentic AIs safely inside Docker

I was working on a project recently that required me to run claude --dangerously-skip-permissions. It turned out fine — this time. But I was low-key sweating the entire time. LLMs are stochastic. I was one bad agentic decision away from disaster. 1,000 things could’ve gone wrong from wiping my hard drive to exfiltrating my AWS secrets and racking up $90k in charges.

The fix: run Claude Code inside a Docker container. Same productivity. Contained blast radius.

And Claude Code isn’t alone here. The same logic applies to any agentic AI system that executes code or manipulates files on your machine — n8n, CrewAI, Openclaw, AutoGen, Aider, OpenHands. If an AI agent can touch your filesystem or run shell commands, it belongs in a container. We’ll use Claude Code as the example throughout, but the pattern is identical for all of them.

What Docker actually buys you

Docker builds a walled sandbox for your AI. Inside the container, Claude Code can do whatever it wants. Outside the container — your real machine — it can’t touch a thing unless you explicitly say so.

Instead of this:

Your Terminal → Claude Code → Your Entire Machine 😬

You get this:

Your Terminal → Docker Container → Only What You Mounted

If Claude goes sideways, you delete the container and start fresh. No drama, no data loss, no explanations to your team.

What Docker protects you from:

What Docker does NOT protect you from:

For local development, it’s still a massive upgrade.

Prerequisites

You’ll need:

Verify Docker is running:

docker --version    # eg. Docker version 29.x.x, build a5c7197

Also create this directory once — it’s where Claude Code will persist its config between container runs:

mkdir -p ~/.claude-sandbox

Step 1: Write the Dockerfile

Create a Dockerfile in a convenient location — a dedicated folder like ~/agent-sandbox/ works well. You don’t need to put it in your project.

FROM node:20-slim

# Install Claude Code globally
RUN npm install -g @anthropic-ai/claude-code

# This is where your project gets mounted
WORKDIR /workspace

# Run Claude Code by default
ENTRYPOINT ["claude"]

That’s it. No exotic base images. No extra tooling. Claude Code is a Node package, so a slim Node image is the right foundation.

Step 2: Build the image

From the folder containing your Dockerfile:

docker build -t agent-sandbox .

The first build takes a minute or two — it’s pulling the base image and installing Claude Code. Subsequent builds are fast thanks to Docker’s layer cache.

Verify it built:

docker images agent-sandbox
# REPOSITORY           ID            DISK USAGE
# agent-sandbox:latest abc123def456  ~300MB

Step 3: Run it against your project

Now the key part: only mount the current folder, nothing else. And run as yourself, not root.

# macOS / Linux
docker run -it --rm \
  --user $(id -u):$(id -g) \
  -e HOME=/claude-home \
  -e ANTHROPIC_API_KEY="$ANTHROPIC_API_KEY" \
  -v ~/.claude-sandbox:/claude-home \
  -v "$(pwd)":/workspace \
  agent-sandbox \
  --dangerously-skip-permissions

Three things worth explaining here:

--user $(id -u):$(id -g) runs the container process as your exact user ID and group ID — not root. Any files Claude creates are owned by you and Claude Code won’t be able to delete anything you can’t.

-v ~/.claude-sandbox:/claude-home mounts a persistent directory from your host as the container’s home. Claude Code writes its config there on the first run and finds it on every run after — so the onboarding interview only happens the first time.

-e HOME=/claude-home tells Claude Code where that home directory is, since your user has no home directory baked into the container image.

Docker Desktop on Windows handles user mapping differently — omit --user and adjust the HOME mount path:

# Windows PowerShell
docker run -it --rm `
  -e HOME=/claude-home `
  -e ANTHROPIC_API_KEY="$env:ANTHROPIC_API_KEY" `
  -v "${env:USERPROFILE}\.claude-sandbox:/claude-home" `
  -v "${PWD}:/workspace" `
  agent-sandbox `
  --dangerously-skip-permissions

What each flag does:

FlagPurpose
-itInteractive terminal (required for Claude Code’s UI)
--rmDelete the container when you exit
--user $(id -u):$(id -g)Run as you, not root
-e HOME=/claude-homeTell Claude Code where its home directory is
-v ~/.claude-sandbox:/claude-homePersist Claude’s config across container runs
-e ANTHROPIC_API_KEYPass your API key into the container
-v "$(pwd)":/workspaceMount your current project directory
--dangerously-skip-permissionsLet Claude run without confirmation prompts

Claude Code starts up inside the container as you, with your project files visible at /workspace. From Claude’s perspective, that’s its entire world.

Step 4: Lock it down further

Mount multiple directories — but only what you need

You can stack as many -v flags as you need. Each one is an explicit decision about what the agent can see.

docker run -it --rm \
  --user $(id -u):$(id -g) \
  -e HOME=/claude-home \
  -v ~/.claude-sandbox:/claude-home \
  -e ANTHROPIC_API_KEY="$ANTHROPIC_API_KEY" \
  -v "$(pwd)":/workspace \
  -v "$(pwd)/reference-docs":/docs:ro \
  -v "$(pwd)/shared-utils":/utils \
  agent-sandbox \
  --dangerously-skip-permissions

The :ro suffix makes a mount read-only inside the container. Use it on anything Claude should read but never modify — specs, design docs, reference implementations. Writable mounts get no suffix.


The rule: every -v flag is a deliberate choice. If you didn’t add it, the agent can’t reach it.


Add resource limits (optional)

If you want to prevent a runaway process from hammering your machine, add --memory and --cpus:

docker run -it --rm \
  --memory 2g --cpus 2 \
  --user $(id -u):$(id -g) \
  -e HOME=/claude-home \
  -v ~/.claude-sandbox:/claude-home \
  -e ANTHROPIC_API_KEY="$ANTHROPIC_API_KEY" \
  -v "$(pwd)":/workspace \
  agent-sandbox \
  --dangerously-skip-permissions

Two CPUs and 2GB of RAM is plenty for Claude Code’s orchestration work. Skip these flags if you’re on a constrained machine or running a heavy build inside the container.

Step 5: Make it a shell alias

Typing that full docker run command every session gets old fast. Add an alias to your shell config.

# ~/.zshrc or ~/.bashrc
alias claude-safe='docker run -it --rm \
  --user $(id -u):$(id -g) \
  -e HOME=/claude-home \
  -v ~/.claude-sandbox:/claude-home \
  -e ANTHROPIC_API_KEY="$ANTHROPIC_API_KEY" \
  -v "$(pwd)":/workspace \
  agent-sandbox \
  --dangerously-skip-permissions'

Then log off and log on again. Or just reload:

source ~/.zshrc

Now just run claude-safe from any project directory. Same experience as running Claude Code directly — just contained.

Other secrets

If Claude needs a credential (like an API key for a service you’re building against), pass it as a specific -e flag, not by mounting a secrets file.

# Good: explicit, scoped
docker run -it --rm \
  --user $(id -u):$(id -g) \
  -e HOME=/claude-home \
  -v ~/.claude-sandbox:/claude-home \
  -e ANTHROPIC_API_KEY="$ANTHROPIC_API_KEY" \
  -e STRIPE_SECRET_KEY="$STRIPE_SECRET_KEY" \
  -v "$(pwd)":/workspace \
  agent-sandbox \
  --dangerously-skip-permissions

# Bad: broad access to sensitive files
docker run -it --rm \
  -v ~/.aws:/root/.aws \        # ← don't do this
  -v "$(pwd)":/workspace \
  agent-sandbox \
  --dangerously-skip-permissions

Agent containers are disposable

This disposability is a feature, and a cool one. It means every session starts from a clean, known state. No accumulated weirdness.

The --rm flag handles teardown automatically when you exit Claude Code. If something feels off mid-session, just Ctrl+C and start a fresh container. You lose nothing — your project files are on the host, not inside the container.

Quick reference

# One-time setup
mkdir -p ~/.claude-sandbox
docker build -t agent-sandbox .   # run from ~/agent-sandbox/

# Run Claude Code in your current project (macOS/Linux)
docker run -it --rm \
  --user $(id -u):$(id -g) \
  -e HOME=/claude-home \
  -v ~/.claude-sandbox:/claude-home \
  -e ANTHROPIC_API_KEY="$ANTHROPIC_API_KEY" \
  -v "$(pwd)":/workspace \
  agent-sandbox \
  --dangerously-skip-permissions

# Run with additional mounts
docker run -it --rm \
  --user $(id -u):$(id -g) \
  -e HOME=/claude-home \
  -v ~/.claude-sandbox:/claude-home \
  -e ANTHROPIC_API_KEY="$ANTHROPIC_API_KEY" \
  -v "$(pwd)":/workspace \
  -v "$(pwd)/docs":/docs:ro \
  agent-sandbox \
  --dangerously-skip-permissions

# Run with resource limits
docker run -it --rm \
  --memory 2g --cpus 2 \
  --user $(id -u):$(id -g) \
  -e HOME=/claude-home \
  -v ~/.claude-sandbox:/claude-home \
  -e ANTHROPIC_API_KEY="$ANTHROPIC_API_KEY" \
  -v "$(pwd)":/workspace \
  agent-sandbox \
  --dangerously-skip-permissions

# Rebuild after Claude Code updates. Keeps it up to date.
docker build --no-cache -t agent-sandbox .

The --no-cache flag forces Docker to rebuild the container which in turn re-installs the latest version of @anthropic-ai/claude-code.


Docker changes the failure mode from “Claude destroyed my laptop and leaked my secrets” to “Claude did something weird inside a container I can delete.” That’s a trade worth making.

Set it up once. Run claude-safe anywhere. Move on.


Want help with Claude Code or any aspect of agentic AI? Reach out.