| plugins/opencode | ||
| .gitignore | ||
| agent-container.py | ||
| agent.aliases | ||
| Dockerfile | ||
| README.md | ||
agent-container
Run OpenCode, Claude Code, and Pi (pi.dev) inside a shared Arch Linux Docker container that closely mirrors a local development environment, while limiting access to sensitive files on the host.
Features
- Arch Linux-based image with OpenCode, Claude Code, and Pi pre-installed
- Runs as the host user (same username, UID, GID)
- Per-project isolation: Each project gets its own container (identified by project path hash)
- Shared persistent home: All containers mount the same home directory from XDG_DATA_HOME, allowing tools and credentials to persist across projects
- Sudo access: Agents can install project-specific dependencies that persist in the stopped container
- Hard linking support: Can hard link files like
~/.gitconfigto share configurations with containers - Local variants:
opencode-local,claude-local, andpi-localrun the tools directly on the host, bypassing the container - Mounts only the current project directory (same absolute path inside container)
- Security boundary: No access to SSH keys, passwords, or full
$HOME(intentionally prevents remote code pushes)
Install
Clone the repository:
cd ~/Projects/
git clone https://git.jeena.net/jeena/agent-container.git
Source the helper file agent.aliases in your shell configuration (.bashrc or .zshrc):
source ~/Projects/agent-container/agent.aliases
This makes the opencode, claude, pi, and agent-container commands available in new sessions, plus the opencode-local, claude-local, and pi-local variants that run the tools directly on the host.
The container home directory at $XDG_DATA_HOME/agent-container/container-home/ serves as a central $HOME inside every container, independent of which project directory you start in. Everything written to $HOME inside the container persists there.
Environment Variables
Host environment variables matching the following prefixes are automatically forwarded into the container:
OPENCODE_*-- passed through to OpenCodeANTHROPIC_*,CLAUDE_*-- passed through to Claude CodePI_*plus provider key prefixes (ANTHROPIC_*,OPENAI_*,GEMINI_*,GROQ_*,OLLAMA_*, and others) -- passed through to Pi. Alternatively log in once with/logininside the container; credentials persist in the shared container home.
This means inline overrides work as expected: OPENCODE_CONFIG=foo opencode
For persistent environment variables, add them to container-home/.bashrc (or .bash_profile / .profile). Tools run through bash -l inside the container, so standard shell config files are sourced automatically. Default shell config files from /etc/skel are seeded into the container home on first run.
XDG_DATA_HOME: Override default data directory (default:~/.local/share)
Usage
From any project directory:
# Run OpenCode
opencode
# Run Claude Code
claude
# Run Pi
pi
# Run a tool directly on the host, bypassing the container
opencode-local
The image is built automatically on first use if it does not already exist. The tool starts inside the container with the current directory mounted and set as the working directory.
Updating
To rebuild the image with the latest versions of OpenCode, Claude Code, and Pi:
agent-container update
This removes all existing containers and rebuilds the image from scratch. Containers are recreated automatically on the next run. The persistent home directory is not affected.
Purge
To remove all containers, the image, and the persistent home directory:
agent-container purge
Sharing host config files via hard links
The container home at ~/.local/share/agent-container/container-home/ is mounted as /home/<username> inside every container. You can hard link files from your real $HOME into this directory so the container sees them without copying or syncing.
Because both paths live on the same filesystem, a hard link means they are literally the same file -- changes from either side are instantly reflected.
Example for .gitconfig:
mkdir -p ~/.local/share/agent-container/container-home
ln ~/.gitconfig ~/.local/share/agent-container/container-home/.gitconfig
Avoid linking sensitive files such as ~/.ssh/id_* or ~/.gnupg/ -- keeping those out of the container is an intentional security boundary.
Sharing agent state between host and container via symlinks
State directories such as .claude/, .pi/, or .agents/ can be shared between the
containerized tools (claude, pi, opencode) and their local variants
(claude-local, pi-local, opencode-local). Keep the real data in the container home
and symlink to it from your real $HOME:
mkdir -p ~/.local/share/agent-container/container-home
mv ~/.claude ~/.local/share/agent-container/container-home/.claude
ln -s ~/.local/share/agent-container/container-home/.claude ~/.claude
The direction matters: the real directory must live in the container home, and the
symlink must be in the host $HOME. The container mounts the container home as
/home/<username>, so inside the container the path is a regular directory. The host
$HOME is never mounted into the container, so a symlink pointing the other way would
not resolve inside the container.
On the host, ~/.claude resolves to the same data the container sees, so logins,
settings, and history are shared by both sides without copying or syncing. The same
works for single files like .claude.json if a tool needs them on the host too.
Repeat the two steps for other state directories (.pi, .agents,
.config/opencode, ...) as needed.
If XDG_DATA_HOME is set to a non-default location, use that path instead. As with
hard links, never share sensitive files such as ~/.ssh/id_* or ~/.gnupg/.
OpenCode plugins
The repository ships an OpenCode plugin in plugins/opencode/:
osc99-notify.js: sends desktop notifications via the OSC 99 escape sequence when OpenCode needs attention (idle, question, permission prompt, error). Notifications only fire while the terminal is unfocused. Tested with Kitty.
To enable it inside the container, copy it into the OpenCode plugin directory of the container home:
mkdir -p ~/.local/share/agent-container/container-home/.config/opencode/plugins
cp plugins/opencode/osc99-notify.js \
~/.local/share/agent-container/container-home/.config/opencode/plugins/