NPM_CONFIG_PREFIX is now /home/developer/.pi/npm-global, with that
prefix's bin/ prepended to PATH. Without this, 'pi install npm:<pkg>'
(and any 'npm install -g') by the developer user would EACCES against
the system prefix (/usr).
The new prefix lives on the devbox-pi-config named volume, so:
- User-installed pi packages (themes, skills, extensions) survive
container recreate AND image rebuild, complementing pi's auto-
restore from settings.json with one less cold-start step.
- A user-driven 'npm install -g @mariozechner/pi-coding-agent' lands
on the volume and wins over the baked pi via PATH order.
Build-time 'npm install -g' calls (opencode, pi, oh-my-opencode-slim)
are unaffected: the new ENVs are declared after those steps in the
Dockerfile, so the baked binaries still install to /usr at build time
and are not shadowed by the volume mount at runtime.
Verified end-to-end with a Bun-driven smoke test: as developer,
'npm install -g cowsay' inside the container succeeds, the binary
lands on PATH, and survives a fresh container against the same volume.
DOCKER_HUB.md regenerated (24997/25000 bytes, 3-byte headroom — was
138 before; future README additions to the persistence section need
to trim something else first).
Docs updated: Dockerfile inline comments, README persistence section,
AGENTS install contract, DOCKER_HUB persistence table, .env.example
notes, CHANGELOG Unreleased entry.
24 KiB
opencode-devbox — Docker Hub
Portable AI developer environment for opencode. Debian-based, with git, SSH, Node.js, AWS CLI v2, and common dev tools pre-installed.
Image Variants
Four image variants are published for each release:
| Tag | Description |
|---|---|
latest / vX.Y.Z |
Base image — opencode, Node.js, AWS CLI, dev tools |
latest-omos / vX.Y.Z-omos |
Base + oh-my-opencode-slim multi-agent orchestration and Bun |
latest-with-pi / vX.Y.Z-with-pi |
Base + pi as alternative/complementary harness (shares the mempalace install with opencode) |
latest-omos-with-pi / vX.Y.Z-omos-with-pi |
OMOS + pi together |
All variants support linux/amd64 and linux/arm64.
NOTE: This file is auto-generated from
README.mdbyscripts/generate-dockerhub-md.py. Edit README.md and regenerate rather than editing this file directly.
Quick Start
docker run -it --rm \
-e ANTHROPIC_API_KEY=your-key \
-e OPENCODE_PROVIDER=anthropic \
-e GIT_USER_NAME="Your Name" \
-e GIT_USER_EMAIL="you@example.com" \
-v ~/projects:/workspace \
-v ~/.ssh:/home/developer/.ssh:ro \
joakimp/opencode-devbox:latest
This drops you straight into opencode with your project mounted at /workspace.
For an interactive shell first (useful for AWS SSO login):
docker run -it --rm \
-e ANTHROPIC_API_KEY=your-key \
-e OPENCODE_PROVIDER=anthropic \
-v ~/projects:/workspace \
-v ~/.ssh:/home/developer/.ssh:ro \
joakimp/opencode-devbox:latest bash
Then run opencode when ready.
For docker-compose users, see the source repo for docker-compose.yml and .env.example templates.
Features
- Debian trixie base — glibc, full PTY/terminal support
- Configurable providers — Anthropic, OpenAI, AWS Bedrock via env vars
- Host filesystem access — bind mount any directory as
/workspace - SSH key forwarding — git push/pull to private repos
- MCP server support — Node.js included for
npx-based MCP servers - Non-root user — runs as
developerwith UID auto-matched to workspace owner (sudo available) - Python via uv —
uvpackage manager included; install Python on demand withuv python install - Rust via rustup —
rustup-initincluded; bootstrap Rust on demand withrustup-init -y - Optional runtimes — Python (apt), Go via build args (Node.js always included — required for opencode v1.x)
- Multi-agent orchestration — optional oh-my-opencode-slim integration via build arg
- AWS CLI v2 — built-in SSO/Bedrock authentication with headless device-code flow
- Multi-arch — amd64 and arm64
Usage
Prerequisites
Bind-mounted directories must exist on the host before starting the container. Docker creates missing directories as root-owned, which causes permission issues.
# Required: workspace for your projects
mkdir -p ~/projects
Connecting to the container
From your laptop, SSH into the remote server where Docker is running, then start the container:
# 1. SSH into the remote server
ssh user@remote-server
# 2. Navigate to the project
cd opencode-devbox
# 3. Start the container with an interactive shell
docker compose run --rm devbox bash
# You're now inside the container — run commands here
aws sso login --sso-session <your-sso-session> --use-device-code
opencode
Running modes
Interactive shell — enter the container, run multiple commands:
docker compose run --rm devbox bash
Direct to opencode — skips the shell, launches opencode immediately:
docker compose run --rm devbox
Background container — keep it running, attach when needed:
# Start in background
docker compose up -d
# Attach a shell to the running container
docker compose exec -u developer devbox bash
# Or run a single command inside it
docker compose exec -u developer devbox aws --version
runcreates a new container (cleaned up with--rm).execattaches to an already running one.
Configuration
Environment Variables
| Variable | Description | Default |
|---|---|---|
OPENCODE_PROVIDER |
LLM provider (anthropic, openai, amazon-bedrock) |
anthropic |
OPENCODE_MODEL |
Model override | Provider default |
ANTHROPIC_API_KEY |
Anthropic API key | — |
OPENAI_API_KEY |
OpenAI API key | — |
AWS_REGION |
AWS region for Bedrock | us-east-1 |
AWS_PROFILE |
AWS SSO profile name | default |
GIT_USER_NAME |
Git commit author name | — |
GIT_USER_EMAIL |
Git commit author email | — |
WORKSPACE_PATH |
Host path to mount | . |
SSH_KEY_PATH |
Host SSH key directory | ~/.ssh |
USER_UID |
Override container user UID | Auto-detect from /workspace |
USER_GID |
Override container user GID | Auto-detect from /workspace |
LANG |
System locale | en_US.UTF-8 |
LANGUAGE |
Language priority list | en_US:en |
LC_ALL |
Override all locale settings | en_US.UTF-8 |
EDITOR |
Default text editor | nvim |
ENABLE_OMOS |
Enable oh-my-opencode-slim multi-agent orchestration | false |
OMOS_TMUX |
Enable tmux pane integration for OMOS | false |
OMOS_SKILLS |
Install OMOS recommended skills on first run | true |
OMOS_RESET |
Force regenerate OMOS config on next start | false |
SKILLSET_CONTAINER_PATH |
Path to skillset repo inside container (for auto-deploy when not at /workspace/skillset) | Auto-detect |
Custom opencode config
Opencode configuration is persisted automatically via the named volume devbox-opencode-config. This volume is mounted at /home/developer/.config/opencode by default — no host directory setup required. All changes to opencode.jsonc, skills, and (on the OMOS variant) oh-my-opencode-slim.json survive container recreation.
When an existing opencode.jsonc is found in the volume, the OPENCODE_PROVIDER auto-config is skipped.
Alternative: host bind-mount — if you specifically want to share config from the host (e.g. to version-control it or sync across machines), replace the named volume with a bind mount:
volumes:
- ~/.config/opencode:/home/developer/.config/opencode
Portability note: The mounted config runs inside a Linux container. Any absolute paths inside
opencode.jsonc(for example, host-specificpluginentries likefile:///usr/local/lib/node_modules/...orfile:///opt/homebrew/...) will not resolve inside the container. Prefer bare package specifiers (e.g."oh-my-opencode-slim") that resolve vianode_moduleslookup, which works on both macOS and Linux hosts.
Custom skills
Skills are deployed automatically from a skillset repo on container start. The entrypoint detects the skillset location in this order:
SKILLSET_CONTAINER_PATHenv var (explicit path to skillset repo inside container)~/skillsetmount (if present)/workspace/skillsetfallback (if your workspace contains askillset/directory)
When a skillset repo is detected, its skills are symlinked into ~/.agents/skills/ automatically. No manual configuration needed.
Warning: Do not bind-mount a host
~/.agents/skillsdirectory directly into the container. This conflicts with the symlink-based auto-deploy mechanism and causes broken skill references.
Neovim configuration
The image includes neovim 0.12 with EDITOR=nvim set by default. To use your own neovim config (and have plugins auto-install via lazy.nvim on first start), mount it from the host:
volumes:
- ~/.config/nvim:/home/developer/.config/nvim:ro
Python development with uv
The image includes Python 3.13 (from Debian Trixie) and uv, a fast Python package manager that replaces pip, venv, and pyenv:
# Python 3.13 is available out of the box
python3 --version
# Use uv for package management
uv venv
uv pip install -r requirements.txt
# Or use uv's project workflow (reads pyproject.toml)
uv sync
# Run a Python script
uv run python script.py
# Install standalone Python tools
uvx ruff check .
# Install a newer Python version (persists with devbox-uv volume)
uv python install 3.14
Python installations are stored in ~/.local/share/uv/. To persist them across container restarts, add the devbox-uv named volume to your docker-compose.yml:
volumes:
- devbox-uv:/home/developer/.local/share/uv
volumes:
devbox-uv:
Project virtual environments (.venv) are stored in your workspace directory and persist automatically via the /workspace bind mount.
Rust development with rustup
The image includes rustup-init, the Rust toolchain installer. Rust is not pre-installed but can be bootstrapped on demand:
# One-time setup: install Rust toolchain (~300MB, persists with volumes)
rustup-init -y
source ~/.cargo/env
# Now use Rust normally
cargo new my-project
cargo build
cargo run
To persist Rust toolchains and cargo data across container restarts, add named volumes to your docker-compose.yml:
volumes:
- devbox-rustup:/home/developer/.rustup
- devbox-cargo:/home/developer/.cargo
volumes:
devbox-rustup:
devbox-cargo:
JavaScript and TypeScript
The base image includes Node.js 22 and npm — sufficient for most JavaScript and TypeScript development:
# Initialize a new project
npm init -y
# Install dependencies
npm install
# Run TypeScript (via tsx, ts-node, etc.)
npx tsx src/index.ts
# Use npx for one-off tools
npx tsc --init
The OMOS image variant also includes Bun, a faster JavaScript runtime and package manager:
bun init
bun install
bun run src/index.ts
Node modules are stored in your project directory under /workspace and persist automatically.
VS Code integration
VS Code can connect directly to a running opencode-devbox container for a full IDE experience with IntelliSense, debugging, and extensions running inside the container.
Local Docker (Docker running on your workstation):
- Install the Dev Containers extension
- Start the container:
docker compose up -d - In VS Code:
Ctrl+Shift+P→ "Dev Containers: Attach to Running Container" → selectopencode-devbox
Remote Docker (Docker running on a remote server, e.g. via SSH):
- Install the Remote - SSH and Dev Containers extensions
- Connect to the remote host:
Ctrl+Shift+P→ "Remote-SSH: Connect to Host" - On the remote host, start the container:
docker compose up -d - In VS Code (now connected to the remote):
Ctrl+Shift+P→ "Dev Containers: Attach to Running Container"
VS Code extensions installed inside the container persist as long as the container exists (not removed with docker compose down). For persistent extension storage across container recreations, add a named volume:
volumes:
- devbox-vscode:/home/developer/.vscode-server
oh-my-opencode-slim (Multi-Agent Orchestration)
oh-my-opencode-slim adds a multi-agent layer on top of opencode — an Orchestrator delegates tasks to specialized agents (Explorer, Oracle, Librarian, Designer, Fixer), each configurable with different models and providers.
Setup
A pre-built OMOS image is available on Docker Hub as joakimp/opencode-devbox:latest-omos. Alternatively, build from source:
1. Build the image with OMOS support:
docker compose build --build-arg INSTALL_OMOS=true
This installs Bun and the oh-my-opencode-slim package into the image.
2. Enable in .env:
ENABLE_OMOS=true
3. Run as normal:
docker compose run --rm devbox
On first start, the entrypoint runs the oh-my-opencode-slim installer in non-interactive mode. It generates agent configuration at ~/.config/opencode/oh-my-opencode-slim.json inside the container. The default preset uses OpenAI models — edit the generated config or mount your own to customize.
OMOS Environment Variables
| Variable | Default | Description |
|---|---|---|
ENABLE_OMOS |
false |
Activate oh-my-opencode-slim on container start |
OMOS_TMUX |
false |
Enable tmux pane integration (tmux is included in the base image) |
OMOS_SKILLS |
true |
Install recommended skills (simplify, agent-browser, cartography) |
OMOS_RESET |
false |
Force regenerate config on next start (backs up existing config) |
Custom Configuration
If you mount the opencode config directory (see Custom opencode config above), the oh-my-opencode-slim.json file is included and persists across restarts. Edit it directly to control which models power each agent, fallback chains, council setup, and more.
See the oh-my-opencode-slim configuration docs for the full reference.
Verifying Agents
After starting opencode with OMOS enabled, run inside the opencode session:
ping all agents
All six agents should respond if your provider authentication is working.
pi (alternative/complementary harness)
pi is a lightweight TUI coding-agent that can run alongside opencode in the same container. Both harnesses share the same mempalace install and palace data — wing/diary entries created by one are visible to the other. Available on the *-with-pi and *-omos-with-pi image tags.
Run
The default container CMD is bash -l, so compose run --rm devbox drops to a login shell. From there pick a harness:
docker compose run --rm devbox # bash — then run `pi`, `opencode`, or `aws sso login`
docker compose run --rm devbox pi # launch pi directly
docker compose exec -u developer devbox pi # attach into a `compose up -d` container
MemPalace integration
pi ships pre-wired with the mempalace bridge — the mempalace.ts extension is symlinked into ~/.pi/agent/extensions/ at first start, exposing palace MCP tools (search, diary, knowledge graph) to pi natively. The same ~/.mempalace/ palace is shared with opencode, so memories are mutually visible across harnesses. To persist palace data across container recreate, uncomment the devbox-palace volume in docker-compose.yml (see MemPalace section below).
Persistence
| Path in container | Volume | Contains |
|---|---|---|
/home/developer/.pi |
devbox-pi-config (default) |
settings.json, agent/extensions/, auth.json, sessions/, plus git/ and npm-global/ (NPM_CONFIG_PREFIX points here) so pi install npm: / git: and npm i -g survive container recreate and image rebuild. |
/home/developer/.mempalace |
devbox-palace (uncomment) |
Shared palace — visible to pi and opencode |
Baked pi (/usr/bin/pi, /opt/pi-*) ships in the image; rebuild to upgrade. npm i -g @mariozechner/pi-coding-agent lands on the volume and wins via PATH.
Full build args, extension list, and toolkit detail: https://gitea.jordbo.se/joakimp/opencode-devbox#pi-alternativecomplementary-harness
AWS Bedrock Authentication
When using AWS Bedrock as your LLM provider, you need:
1. AWS config on the host
The container needs access to your ~/.aws/config with SSO session configuration. If you already have this on another machine, copy it:
scp -r user@other-machine:~/.aws ~/.aws
Or configure from scratch on the host:
aws configure sso
2. Mount ~/.aws into the container
Uncomment the AWS volume mount in docker-compose.yml:
- ~/.aws:/home/developer/.aws
Note: do not use :ro — SSO writes token cache files to this directory.
3. Authenticate inside the container
Since the container runs headless (no browser), use the device-code flow:
# Start the container
docker compose up -d
docker compose exec -u developer devbox bash
# Authenticate — prints a URL and code you open in your local browser
aws sso login --sso-session <your-sso-session> --use-device-code
# Once approved in the browser, start opencode
opencode
The --use-device-code flag outputs a URL and short code instead of trying to open a browser. Copy the URL into any browser (on your laptop, phone, etc.), enter the code, and complete the 2FA flow. The CLI in the container picks up the session automatically.
SSO sessions typically last 8–12 hours before requiring re-authentication. Since ~/.aws is mounted from the host, tokens persist across container restarts.
MemPalace — persistent AI memory
The image includes MemPalace, a local-first AI memory system that stores conversation history verbatim and retrieves it via semantic search. Nothing leaves your machine.
MemPalace adds ~300 MB to the image (chromadb, embedding model deps). If you don't use it, rebuild with
--build-arg INSTALL_MEMPALACE=falseto shrink the image.
Enabling persistence
Uncomment the palace volume in docker-compose.yml:
- devbox-palace:/home/developer/.mempalace
Without the volume, palace data lives in the container's writable layer and is lost on --force-recreate.
MCP integration with opencode
Add mempalace as an MCP server in your opencode.jsonc (inside ~/.config/opencode/):
{
"mcp": {
"mempalace": {
"type": "local",
"command": ["mempalace-mcp"]
}
}
}
The image installs mempalace into an isolated
uv toolvenv at/opt/uv-tools/mempalace/.uv tool installplacesmempalace-mcponPATHas a shim whose shebang points at the venv's Python, so MCP clients can invoke it as a normal binary without worrying about the venv. Do not use["python3", "-m", "mempalace.mcp_server"]— the system Python cannot import from the uv-managed venv and you'll getModuleNotFoundError/MCP error -32000: connection closed.
This gives opencode access to 29 MCP tools for searching memory, querying the knowledge graph, managing wings/rooms/drawers, and agent diaries.
Basic usage
# Mine project files into the palace
mempalace mine /workspace
# Mine conversation transcripts
mempalace mine ~/.local/share/opencode/ --mode convos
# Search memory
mempalace search "why did we switch to eno1"
# Load context for a new session
mempalace wake-up
Each workspace gets its own isolated "wing" — memories never leak between projects.
Scheduled mining (mempalace-toolkit)
The image bakes in mempalace-toolkit, a small set of bash wrappers that pair with mempalace for two common routines:
# Mine opencode session history (reads ~/.local/share/opencode/opencode.db, stages JSONL, mines into wing_conversations)
mempalace-session
# Mine a project's docs into a dedicated wing
mempalace-docs /workspace/my-project
Both wrappers are idempotent and dedup-aware — re-running them on unchanged input is a cheap no-op.
For weekly automated runs, the toolkit ships ready-to-use scheduler templates (systemd user timer, launchd user agent, cron) in its contrib/ directory. The *-devbox variants are designed for this container: host-side schedulers that docker exec into the running opencode-devbox.
Disable the toolkit (keeps mempalace itself) with --build-arg INSTALL_MEMPALACE_TOOLKIT=false. Pin to a specific ref with --build-arg MEMPALACE_TOOLKIT_REF=v0.3.0 once tagged releases exist.
Storage
Two separate named volumes keep different data classes apart:
- Palace data (
~/.mempalace/): ChromaDB vectors, SQLite knowledge graph, drawers. This is your memory — back it up, treat it as precious. Persists via thedevbox-palacenamed volume. - Embedding model cache (
~/.cache/chroma/): ONNX model (~79 MB), downloaded automatically on first search. Disposable — blow it away and it re-downloads in ~4 seconds. Persists via thedevbox-chroma-cachenamed volume so you don't re-download on every container recreation. - No API keys required for core functionality (local embeddings via ONNX).
Both volumes are commented out by default in docker-compose.yml — uncomment to enable:
- devbox-palace:/home/developer/.mempalace
- devbox-chroma-cache:/home/developer/.cache/chroma
Air-gapped environments: pre-populate the devbox-chroma-cache volume with the all-MiniLM-L6-v2/ model contents. The palace volume needs no pre-population.
Gitea MCP server
The image includes the official Gitea MCP server (gitea-mcp), providing 50+ MCP tools for interacting with self-hosted Gitea instances — repositories, issues, pull requests, releases, branches, wiki, and Actions.
Setup
-
Create a Personal Access Token on your Gitea instance (Settings → Applications → Generate Token, scopes:
repo,read:user). -
Add to your
.env:GITEA_HOST=https://your-gitea-instance.example.com GITEA_ACCESS_TOKEN=your_token_here -
Enable the gitea MCP server in your
opencode.jsonc:{ "mcp": { "gitea": { "type": "local", "command": ["gitea-mcp", "-t", "stdio", "--host", "{env:GITEA_HOST}"], "environment": { "GITEA_ACCESS_TOKEN": "{env:GITEA_ACCESS_TOKEN}" }, "enabled": true } } }
The server is installed but disabled by default — it requires authentication to be useful.
Architecture
Host Machine
├── ~/projects/my-app ──bind mount──▶ /workspace (container)
├── ~/.ssh ──bind mount──▶ /home/developer/.ssh (ro)
├── ~/.aws ──bind mount──▶ /home/developer/.aws (Bedrock SSO)
└── .env ──env vars───▶ provider config + API keys
Container (Debian trixie)
├── opencode binary
├── oh-my-opencode-slim (optional — multi-agent orchestration plugin, includes Bun)
├── AWS CLI v2 (SSO + Bedrock auth)
├── neovim 0.12, tmux, htop, bat, eza, zoxide, uv, rustup, make, gcc, g++, rsync
├── git, git-crypt, age, ssh, ripgrep, fd, fzf, jq, curl, tree
├── Node.js (for MCP servers)
├── Bun (optional — included with oh-my-opencode-slim)
├── entrypoint.sh (UID adjustment, git config, provider setup)
└── /workspace ← your code lives here
Data persistence
| Path in container | Source | Survives --rm? |
Contains |
|---|---|---|---|
/workspace |
Host bind mount | ✅ Yes | Your project files |
/home/developer/.ssh |
Host bind mount (ro) | ✅ Yes | SSH keys |
/home/developer/.aws |
Host bind mount (if configured) | ✅ Yes | AWS credentials/SSO cache |
/home/developer/.local/share/opencode |
Named volume devbox-data |
✅ Yes | Session history, memory |
/home/developer/.local/state/opencode |
Named volume devbox-state |
✅ Yes | TUI settings (theme, toggles) |
/home/developer/.cache/bash |
Named volume devbox-shell-history |
✅ Yes | Bash history ($HISTFILE), survives container recreate |
/home/developer/.local/share/zoxide |
Named volume devbox-zoxide |
✅ Yes | Zoxide directory history (z <fragment> jump targets) |
/home/developer/.local/share/nvim |
Named volume devbox-nvim-data |
✅ Yes | Neovim plugins, Mason LSP installs, Lazy plugin cache |
/home/developer/.local/share/uv |
Named volume devbox-uv (if configured) |
✅ Yes | Python installs, uv tool installs |
/home/developer/.rustup |
Named volume devbox-rustup (if configured) |
✅ Yes | Rust toolchains |
/home/developer/.cargo |
Named volume devbox-cargo (if configured) |
✅ Yes | Cargo binaries, registry cache |
/home/developer/.vscode-server |
Named volume devbox-vscode (if configured) |
✅ Yes | VS Code server and extensions |
/home/developer/.config/opencode |
Named volume devbox-opencode-config |
✅ Yes | opencode.jsonc, skills, plus oh-my-opencode-slim.json on the OMOS variant |
opencode config (opencode.jsonc) is auto-generated from OPENCODE_PROVIDER on each start. It sets provider and model only — no MCP servers. To persist config changes and use custom settings, use the named volume (default) or bind-mount from host (see Custom opencode config above).
Source
MIT licensed. Source, issues, and docker-compose.yml templates: https://gitea.jordbo.se/joakimp/opencode-devbox