Run OpenCode inside a Docker container with sandboxed file access and security hardening.
- Runs OpenCode in an isolated Docker container
- Mounts your current working directory at a stable unique path under
/workspaces/... - Persistent home directory and configuration across sessions
- Security hardening (dropped capabilities, no-new-privileges)
- Pre-installed tools: git, ripgrep, fzf, curl, Python, BasedPyright, Terraform, Terraform LS, Node.js language servers, and more
- oh-my-openagent plugin for multi-agent orchestration
- Internal tmux support for oh-my-openagent team-mode/hyperplan workflows
- Web UI mode for browser-based access
- Build the image:
./build - Run the container:
./ocd - Or start with internal tmux:
./ocd --tmux→ opens OpenCode inside a container tmux session - Or start in web mode:
./ocd --web→ opens web UI at http://localhost:4096
Optional: Symlink to run from anywhere:
ln -s "$(pwd)/ocd" ~/.local/bin/ocd| Path | Description |
|---|---|
config/opencode.json |
Base config |
config/opencode.local.json |
Local overrides (gitignored) |
config/opencode.merged.json |
Auto-merged result (gitignored) |
config/oh-my-openagent.*.json |
Model profiles (see below) |
config/tui.json |
TUI theme/config mounted into the container |
config/tmux.conf |
Internal tmux config mounted as /home/coder/.tmux.conf |
config/agents/*.md |
Wrapper-level custom OpenCode agents |
data/ |
Persistent home directory |
Create config/opencode.local.json to override settings without committing:
{ "provider": { "apiKey": "sk-secret-key" } }On startup, ocd recursively merges this on top of opencode.json using jq. Objects are merged recursively, arrays are appended with duplicate entries skipped, and scalar values from the local file replace base values. The merged result is mounted read-only into the container.
The base config/opencode.json also carries shell permissions. Current defaults allow bash commands broadly while denying git push, sudo, and su patterns.
If /tmp/.X11-unix exists on the host, it's automatically mounted (read-only) with DISPLAY for clipboard sharing. Skipped on Wayland-only or macOS hosts.
Pre-installed plugin providing multi-agent orchestration (Sisyphus, Oracle, Librarian, etc.), background agents, LSP/AST tools, and ultrawork command.
The image includes Python as both python3 and python, BasedPyright's basedpyright-langserver, and Terraform tooling as both terraform and terraform-ls, so OpenCode can run common Python tests, Python LSP diagnostics, Terraform formatting, and Terraform LSP diagnostics inside the container after rebuilding with ./build.
The image includes tmux for oh-my-openagent team-mode/hyperplan workflows. Team-mode and top-level tmux.enabled integration are enabled in the committed model profiles. The wrapper mounts config/tmux.conf read-only as /home/coder/.tmux.conf, so tmux sessions created by OpenCode use the repo config inside the container.
Use ./ocd --tmux to start OpenCode inside a visible container-internal tmux session named opencode. Extra OpenCode arguments are forwarded after the workspace path, and --profile still selects the mounted oh-my-openagent profile:
./ocd --tmux
./ocd --tmux --profile minimaxThis tmux setup is intentionally internal to the container. It does not share host tmux sockets or sessions, so you can launch ./ocd --tmux from a host tmux pane while oh-my-openagent uses its own separate tmux server inside Docker. --tmux is for the terminal TUI and cannot be combined with --web. Rebuild with ./build after changing the Dockerfile or tmux package set.
When --tmux is used, the launcher starts OpenCode with --port because oh-my-openagent tmux pane spawning requires an OpenCode server port. The port defaults to 4096 and can be changed with --port, the same flag used by web mode.
The launcher pins the container's outer TERM to xterm-256color; tmux then sets its own terminal type inside the session. This avoids broken rendering when the host uses a terminal name that is not available in Debian terminfo.
For team-mode pane visualization, the launcher also exports the active tmux pane id before starting OpenCode, so oh-my-openagent can resolve the caller pane and split the correct window.
Define wrapper-level custom OpenCode agents as Markdown files under config/agents/. The ocd launcher mounts that directory read-only to /config/agents, and OPENCODE_CONFIG_DIR=/config lets OpenCode load them alongside the JSON config.
Each file name becomes the agent name. For example, config/agents/reviewer.md creates an agent named reviewer:
---
description: Reviews changes for bugs and missing tests
mode: subagent
model: openai/gpt-5.5
temperature: 0.1
permission:
edit: deny
---
Review the current changes. Focus on correctness, regressions, security issues,
and missing verification. Report findings first, ordered by severity.Use project-level .opencode/agents/*.md files in the workspace for agents that should live with one project. Use this repo's config/agents/*.md for agents you want available whenever you launch through ocd. Avoid naming custom agents the same as built-in agents unless you intentionally want to override them.
This wrapper includes hallucinator, a high-temperature primary agent for speculative ideation and playful brainstorming. Use it when you want more creative, less grounded output; switch back to a grounded agent before relying on factual claims or implementation details.
Switch models via --profile flag:
./ocd # Use default OpenAI models
./ocd --profile minimax # Use MiniMax models
./ocd --profile ollama # Use local Ollama models onlyAvailable profiles:
| Profile | Description |
|---|---|
| (default) | Uses OpenAI models exclusively |
minimax |
Uses MiniMax models for most agents (with OpenAI fallbacks) |
ollama |
Uses the local Ollama-only profile in config/oh-my-openagent.ollama.json with the Ollama provider from config/opencode.json |
The committed Ollama profile maps all agents/categories to the local gemma4:26b-16k model by default.
To create a new profile, copy config/oh-my-openagent.json to config/oh-my-openagent.<profile>.json and modify the model assignments.
Start OpenCode with a browser-based UI instead of the terminal TUI:
./ocd --web # Web UI on http://localhost:4096
./ocd --web --port 8080 # Custom port
./ocd --web --profile minimax # Combine with model profilesWeb mode cannot be combined with --tmux; tmux mode is only for the terminal TUI.
Port auto-detection: If the default port is already in use (e.g., another ocd --web instance), it automatically finds the next available port and prints which one it chose.
Authentication (optional): Set OPENCODE_SERVER_PASSWORD to require basic auth:
OPENCODE_SERVER_PASSWORD=secret ./ocd --webUsername defaults to OpenCode's built-in opencode value unless OPENCODE_SERVER_USERNAME is set.
Environment variables:
| Variable | Description | Default |
|---|---|---|
OCD_WEB_PORT |
Default web port (overridden by --port) |
4096 |
OPENCODE_SERVER_PASSWORD |
Basic auth password | (none — unauthenticated) |
OPENCODE_SERVER_USERNAME |
Basic auth username | opencode |
.
├── build # Build the Docker image
├── ocd # Run the container
├── clearcache # Clear caches
├── config/ # OpenCode and oh-my-openagent configs
│ ├── opencode.json # Base config (committed)
│ ├── opencode.local.json # Local overrides (gitignored, optional)
│ ├── opencode.merged.json # Merged result (gitignored, auto-generated)
│ ├── oh-my-openagent.json # oh-my-openagent default config — OpenAI only (committed)
│ ├── oh-my-openagent.minimax.json # oh-my-openagent MiniMax profile (committed)
│ ├── oh-my-openagent.ollama.json # oh-my-openagent Ollama-only profile (committed)
│ ├── oh-my-openagent.local.json # oh-my-openagent local overrides (gitignored, optional)
│ ├── oh-my-openagent.merged.json # oh-my-openagent merged result (gitignored, auto-generated)
│ ├── tmux.conf # Internal tmux config mounted to /home/coder/.tmux.conf
│ └── agents/ # Markdown custom agents mounted to /config/agents
├── data/ # Persistent home (mounted to /home/coder)
└── Dockerfile # Container definition
The ocd script:
- Builds/runs
ocd:latestDocker image - Generates unique container name per run
- Mounts the current physical directory to a deterministic
/workspaces/<basename>-<hash>path so new OpenCode sessions scope correctly with newer session behavior - Mounts config files to
/config(setsOPENCODE_CONFIGandOPENCODE_CONFIG_DIR, includingtui.json) - Mounts
config/agentsto/config/agentsso Markdown custom agents are available in everyocdsession - Mounts
config/tmux.confto/home/coder/.tmux.conffor container-internal tmux sessions - Clears the image
opencodeentrypoint at launch, then explicitly runs eitheropencode,opencode web, ortmux - Applies security restrictions (dropped capabilities, no-new-privileges)
Old sessions are not migrated; this only affects new launches.
For Ollama, increase context window size for large codebases and save a larger-context variant of the model you want to use:
$ ollama run gemma4:26b
>>> /set parameter num_ctx 16384
>>> /save gemma4:26b-16k
>>> /byeThis matches the committed local profile in config/oh-my-openagent.ollama.json and the Ollama model entry in config/opencode.json.
| Context Size | Use Case |
|---|---|
| 8192 | Small projects, single files |
| 16384 | Most coding tasks |
| 32768 | Large codebases, multi-file refactoring |
Larger contexts need more VRAM (~2-4GB extra for 16K).