# opencode-devbox Portable AI developer environment in a Docker container. Run [opencode](https://opencode.ai) on any Docker-capable machine with configurable LLM providers, dev tools, and host filesystem access. ## Why? The official `ghcr.io/anomalyco/opencode` image (now archived) was Alpine-based and minimal — no git, no dev tools, broken PTY support due to musl/glibc incompatibility. This project provides a **Debian-based, production-ready** alternative using the current v1.x release. ## Quick Start ```bash # Clone git clone ssh://gitea.jordbo.se:2222/joakimp/opencode-devbox.git cd opencode-devbox # Configure cp .env.example .env # Edit .env with your provider, API key, workspace path, git config # Install git hooks (secret scanning) brew install gitleaks # macOS / Linuxbrew ./setup-hooks.sh # Build and run docker compose run --rm devbox ``` ## 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 `developer` with UID auto-matched to workspace owner (sudo available) - **Python via uv** — `uv` package manager included; install Python on demand with `uv python install` - **Rust via rustup** — `rustup-init` included; bootstrap Rust on demand with `rustup-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](https://github.com/alvinunreal/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. ```bash # Required: workspace for your projects mkdir -p ~/projects # If mounting opencode config (recommended for persistent settings) mkdir -p ~/.config/opencode ``` ### Connecting to the container From your laptop, SSH into the remote server where Docker is running, then start the container: ```bash # 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 --use-device-code opencode ``` ### Running modes **Interactive shell** — enter the container, run multiple commands: ```bash docker compose run --rm devbox bash ``` **Direct to opencode** — skips the shell, launches opencode immediately: ```bash docker compose run --rm devbox ``` **Background container** — keep it running, attach when needed: ```bash # 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 ``` > `run` creates a new container (cleaned up with `--rm`). `exec` attaches 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` | ### Custom opencode config For full control over opencode settings (MCP servers, custom models, and — on the OMOS variant — oh-my-opencode-slim agents), mount the entire config directory from the host: ```yaml volumes: - ~/.config/opencode:/home/developer/.config/opencode ``` This persists all configuration changes across container restarts, including `opencode.json`, skills, and (on the OMOS variant) `oh-my-opencode-slim.json`. When an existing `opencode.json` is found, the `OPENCODE_PROVIDER` auto-config is skipped. > **Portability note:** The mounted config runs inside a Linux container. Any absolute paths inside `opencode.json` (for example, host-specific `plugin` entries like `file:///usr/local/lib/node_modules/...` or `file:///opt/homebrew/...`) will not resolve inside the container. Prefer bare package specifiers (e.g. `"oh-my-opencode-slim"`) that resolve via `node_modules` lookup, which works on both macOS and Linux hosts. ### Custom skills Mount agent skills from the host: ```yaml volumes: - ~/.agents/skills:/home/developer/.agents/skills:ro ``` ### 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: ```yaml volumes: - ~/.config/nvim:/home/developer/.config/nvim:ro ``` ### Python development with uv The image includes Python 3.13 (from Debian Trixie) and [uv](https://docs.astral.sh/uv/), a fast Python package manager that replaces pip, venv, and pyenv: ```bash # 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`: ```yaml 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: ```bash # 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`: ```yaml 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: ```bash # 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: ```bash 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):** 1. Install the [Dev Containers](https://marketplace.visualstudio.com/items?itemName=ms-vscode-remote.remote-containers) extension 2. Start the container: `docker compose up -d` 3. In VS Code: `Ctrl+Shift+P` → "Dev Containers: Attach to Running Container" → select `opencode-devbox` **Remote Docker (Docker running on a remote server, e.g. via SSH):** 1. Install the [Remote - SSH](https://marketplace.visualstudio.com/items?itemName=ms-vscode-remote.remote-ssh) and [Dev Containers](https://marketplace.visualstudio.com/items?itemName=ms-vscode-remote.remote-containers) extensions 2. Connect to the remote host: `Ctrl+Shift+P` → "Remote-SSH: Connect to Host" 3. On the remote host, start the container: `docker compose up -d` 4. 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: ```yaml volumes: - devbox-vscode:/home/developer/.vscode-server ``` ### Shared machine setup (multiple users, single OS account) For machines where multiple users share one OS account (e.g. a common `garage` user), a separate compose file isolates each user's config and data using a `SIGNUM` variable. Each user creates their own directory and setup: ```bash # Replace with your username/identifier mkdir -p ~//opencode-devbox cd ~//opencode-devbox # Copy the shared-machine compose and env files cp /path/to/opencode-devbox/docker-compose.shared.yml docker-compose.yml cp /path/to/opencode-devbox/.env.shared.example .env # Create per-user config directory mkdir -p ~//.config/opencode # Edit .env with your signum, provider, keys, etc. vim .env # Start docker compose up -d docker compose exec -u developer devbox- opencode ``` Each user's container, config, and named volumes are fully isolated: - Container name: `devbox-` (no collisions) - Named volumes: prefixed with the project directory name (automatic per-user isolation) - Opencode config: `~//.config/opencode/` (per-user settings, OMOS config, etc.) See `docker-compose.shared.yml` and `.env.shared.example` for the full configuration. ### Rebuilding the Image `docker compose run` and `docker compose up` use the existing image — they **do not rebuild** when you change the Dockerfile or build args (e.g. updating `OPENCODE_VERSION`). Rebuild explicitly: ```bash # Rebuild then run docker compose build docker compose run --rm devbox # Or rebuild and run in one step docker compose run --rm --build devbox ``` ### Build Args Enable optional language runtimes or pin a specific opencode version: ```bash docker compose build --build-arg INSTALL_PYTHON=true --build-arg INSTALL_GO=true docker compose build --build-arg OPENCODE_VERSION=1.5.0 ``` | Arg | Default | Description | |---|---|---| | `INSTALL_PYTHON` | `false` | Python 3 + pip + venv | | `INSTALL_GO` | `false` | Go toolchain | | `INSTALL_OMOS` | `false` | [oh-my-opencode-slim](https://github.com/alvinunreal/oh-my-opencode-slim) multi-agent orchestration (installs Bun and plugin) | | `OMOS_VERSION` | `latest` | Pin a specific oh-my-opencode-slim version | ## oh-my-opencode-slim (Multi-Agent Orchestration) [oh-my-opencode-slim](https://github.com/alvinunreal/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:** ```bash 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`:** ```bash ENABLE_OMOS=true ``` **3. Run as normal:** ```bash 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](https://github.com/alvinunreal/oh-my-opencode-slim/blob/master/docs/configuration.md) 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. ## 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: ```bash scp -r user@other-machine:~/.aws ~/.aws ``` Or configure from scratch on the host: ```bash aws configure sso ``` ### 2. Mount `~/.aws` into the container Uncomment the AWS volume mount in `docker-compose.yml`: ```yaml - ~/.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: ```bash # 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 --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. ## Shell defaults The image ships a baked `~/.bash_aliases` and `~/.inputrc` with quality-of-life defaults: - **Prefix history search** on Up/Down arrows (type `git `, press Up, walk back through prior `git ...` commands only). Ctrl-Up / Ctrl-Down still step through full history. - **Persistent history** — `$HISTFILE` points at `~/.cache/bash/history`, backed by the `devbox-shell-history` named volume so history survives container recreation. Timestamps, 100 000 entries, dedup. - **Case-insensitive tab completion**, coloured completion lists, `show-all-if-ambiguous`. - **Aliases** — `ls`/`ll`/`la` use `eza`, `cat` uses `bat`, `gs`/`gd`/`gl` for git, safe `rm`/`mv`/`cp`. - **Integrations** — `zoxide` (`z ` to jump), `fzf` Ctrl-R / Ctrl-T key bindings. - **Prompt marker** — `[devbox]` prefix so it's always obvious you're inside the container. To override with your own host config, uncomment the bind-mount lines in `docker-compose.yml`: ```yaml - ~/.bash_aliases:/home/developer/.bash_aliases:ro - ~/.inputrc:/home/developer/.inputrc:ro ``` ## Secret Scanning A [gitleaks](https://github.com/gitleaks/gitleaks) pre-commit hook prevents accidentally committing API keys, passwords, or other secrets. ### Setup ```bash # macOS / Linuxbrew brew install gitleaks # Debian/Ubuntu (download binary) curl -sSL https://github.com/gitleaks/gitleaks/releases/latest/download/gitleaks_$(uname -s)_$(uname -m).tar.gz | sudo tar -xz -C /usr/local/bin gitleaks ``` The hook runs automatically on every `git commit`. If gitleaks isn't installed, the hook prints a warning and allows the commit (no hard dependency on collaborators). ### Bypass For legitimate cases (test data, documentation with example keys): ```bash git commit --no-verify -m "Add test fixtures" ``` ### Configuration Allowlisted paths and rules are in `.gitleaks.toml`. The defaults extend gitleaks' built-in rules and allow `.env.example` and documentation files. ## 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/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` | Host bind mount (if configured) | ✅ Yes | opencode.json, skills, plus `oh-my-opencode-slim.json` on the OMOS variant | **opencode config** (`opencode.json`) 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, mount the config directory from the host (see Custom opencode config above). ## License MIT