DEV Community

Sayem Chowdhury
Sayem Chowdhury

Posted on

Run Codex, Claude Code, and OpenCode in Docker — Without Giving Them Your Host

AI coding CLIs are powerful — and that power is the problem.

Codex, Claude Code, and OpenCode can read your codebase, run shell commands, install packages, and edit files. When they run directly on your host, a bad prompt, a confused model, or a compromised dependency can touch anything your user account can reach.

I built ai-agents to put that workflow inside Docker instead.

Over 500 developers have already pulled the images. This post explains what it does, what it actually protects, and how to set it up in under two minutes.

The idea

Instead of installing an AI CLI globally and letting it run on your OS:

  1. Pull a pre-built Docker image with the CLI + language tooling baked in
  2. Bind-mount your project so edits are live on disk
  3. Mount your existing login (~/.codex, ~/.claude, etc.) — no API keys in the image
  4. Run from any repo with a shell alias

No repo clone required. No custom Dockerfile per project. One alias in ~/.zshrc and you're done.

┌─────────────────────────────────────────┐
│  Host OS — protected by Docker          │
│  ┌───────────────────────────────────┐  │
│  │  Container                        │  │
│  │  ┌─────────────────────────────┐  │  │
│  │  │  /workspace (your repo)     │  │  │  ← bind-mounted, writable
│  │  │  Codex / Claude / OpenCode  │  │  │
│  │  └─────────────────────────────┘  │  │
│  └───────────────────────────────────┘  │
└─────────────────────────────────────────┘
Enter fullscreen mode Exit fullscreen mode

Docker is the outer sandbox. Inside the container, the CLI still gets full access to /workspace and whatever auth dirs you mount — that's intentional. The agent needs to write code and run tests.

What you get

Three CLIs, one pattern:

Tool Image tag Inner sandbox flag
OpenAI Codex sayem314/ai-agents:codex --sandbox danger-full-access
Claude Code sayem314/ai-agents:claude-code --dangerously-skip-permissions
OpenCode sayem314/ai-agents:opencode (none — Docker is the boundary)

15 published tags total: 3 tools × 5 language stacks.

Lang What's inside
full (default) Node, Python, Go, Rust + Playwright CLI + Chromium
node Node 24, npm, pnpm, bun
python Python 3.14, uv
go Go 1.26
rust Rust stable, clang, lld

Examples: codex, codex-node, claude-python, opencode-rust.

Multi-arch (amd64 + arm64) — works on Apple Silicon and Linux out of the box.

Quick start

Prerequisites: Docker installed, and sign in to your chosen CLI once on the host (so ~/.codex or ~/.claude exists).

Add aliases to ~/.zshrc or ~/.bashrc:

# Codex
alias codex='docker run -it --rm -w /workspace \
  -v "$PWD:/workspace" \
  -v "$HOME/.codex:/root/.codex" \
  -v "$HOME/.config/codex:/root/.config/codex" \
  sayem314/ai-agents:codex --sandbox danger-full-access'

# Claude Code
alias claude-code='docker run -it --rm -w /workspace \
  -v "$PWD:/workspace" \
  -v "$HOME/.claude:/root/.claude" \
  -v "$HOME/.config/claude:/root/.config/claude" \
  sayem314/ai-agents:claude-code --dangerously-skip-permissions'
alias claude='claude-code'

# OpenCode
alias opencode='docker run -it --rm -w /workspace \
  -v "$PWD:/workspace" \
  -v "$HOME/.config/opencode:/root/.config/opencode" \
  -v "$HOME/.local/share/opencode:/root/.local/share/opencode" \
  sayem314/ai-agents:opencode'
Enter fullscreen mode Exit fullscreen mode

Then:

source ~/.zshrc
cd ~/my-app
codex          # or claude-code / opencode
Enter fullscreen mode Exit fullscreen mode

First run pulls the image automatically. Every run is ephemeral (--rm) — no leftover container state.

How auth works

You log in on your host once. The alias mounts those config dirs into the container at /root/...:

Tool Host paths mounted
Codex ~/.codex, ~/.config/codex
Claude Code ~/.claude, ~/.config/claude
OpenCode ~/.config/opencode, ~/.local/share/opencode

No tokens baked into the image. No keys in your shell rc unless you choose API-key auth for CI.

For git push from inside the container, add read-only mounts:

-v "$HOME/.gitconfig:/root/.gitconfig:ro" \
-v "$HOME/.ssh:/root/.ssh:ro"
Enter fullscreen mode Exit fullscreen mode

What Docker actually protects

Protected:

  • Host filesystem outside mounted volumes
  • Host processes, packages, and network namespace (unless you expose ports)
  • Leftover state — container is removed on exit

Not protected:

  • Your bind-mounted repo — the agent can read, edit, and delete project files
  • Mounted auth dirs — writable unless you add :ro
  • Secrets in the repo (.env, keys, credentials)

Treat the agent like a powerful local developer with access to everything you mounted. Review diffs before committing, same as always.

The --sandbox danger-full-access / --dangerously-skip-permissions flags relax the CLI's inner restrictions so it can run builds, tests, and package installs freely inside the container. Docker remains the outer boundary.

Why not just use the official install?

Fair question. Here's the trade-off:

Approach Pros Cons
Native CLI on host Fastest I/O, simplest setup Agent runs with your user permissions everywhere
Official OpenCode Docker image Maintained upstream Single tool, no lang variants, no unified pattern
ai-agents Same alias pattern for 3 CLIs, 5 lang stacks, subscription auth mounts Bind-mount I/O overhead on macOS, runs as root inside container

On macOS, add :cached to volume mounts and enable VirtioFS in Docker Desktop for better performance:

-v "$PWD:/workspace:cached"
Enter fullscreen mode Exit fullscreen mode

Picking an image

Your project Tag
Polyglot / general use codex, claude-code, opencode
Node/TS only *-node
Python only *-python
Go module *-go
Rust crate *-rust

Full images are larger but work everywhere. Language-specific tags pull faster and use less disk.

Need browser automation? Playwright CLI + Chromium ships only on full tags.

What's under the hood

Two-layer build:

  1. Language layer — official base images (node:24-bookworm, python:3.14-bookworm, etc.) + shared deps (git, curl, build tools)
  2. Tool layer — CLI installed via official scripts on top

Codex installs to /opt/codex (not ~/.codex) so mounting ~/.codex for auth doesn't hide the Linux binary. Language layers stay local; only the 15 tool tags are published to Docker Hub.

CI publishes updated images on a staggered schedule — Codex, Claude, and OpenCode each get their own workflow.

Limitations (honest)

  • Runs as root inside the container. Files created in your project may be root-owned on the host. --user $(id -u):$(id -g) won't work today — CLIs live in /root/.local/bin.
  • Not a security silver bullet. If you mount $HOME or your entire disk, you've undone the isolation.
  • Resume/history depends on mounting the correct auth paths and running from the same project directory. Session storage is tool-specific — see the docs for details.

Try it

docker pull sayem314/ai-agents:codex
Enter fullscreen mode Exit fullscreen mode

Or browse the full docs, image matrix, and troubleshooting guide on GitHub:

github.com/sayem314/ai-agents

Star the repo if it's useful — and open an issue if something breaks. 500+ pulls in and I'm still iterating.


Questions? Drop a comment or open a GitHub issue. What AI CLI do you run — and do you sandbox it?

Top comments (1)

Collapse
 
alexshev profile image
Alex Shev

Container boundaries make a lot of sense for coding agents. The goal is not to make the agent powerless; it is to give it enough room to work without giving it the whole machine by default.

Some comments may only be visible to logged-in visitors. Sign in to view all comments.