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:
- Pull a pre-built Docker image with the CLI + language tooling baked in
- Bind-mount your project so edits are live on disk
-
Mount your existing login (
~/.codex,~/.claude, etc.) — no API keys in the image - 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 │ │ │
│ │ └─────────────────────────────┘ │ │
│ └───────────────────────────────────┘ │
└─────────────────────────────────────────┘
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'
Then:
source ~/.zshrc
cd ~/my-app
codex # or claude-code / opencode
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"
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"
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:
-
Language layer — official base images (
node:24-bookworm,python:3.14-bookworm, etc.) + shared deps (git, curl, build tools) - 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
$HOMEor 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
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)
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.