Run opencode inside Docker instead of installing it locally.
README.md(this file): Docker setup, plugins, run flow, auth, config, and security notes..env.dist: template for local environment variables (PLUGINS,OPENCODE_VERSION, etc.).docs/opencode-commands-skills-tools.md: slash commands, skills, and custom tool reference..opencode/config/AGENTS.md: default agent behavior and skill loading policy.
# 1) Build image (run once, or after Dockerfile changes)
make opencode-build
# 2) Start container
make opencode-run
# -> http://localhost:4096
# 3) Stop and remove container
make opencode-downBefore first run, copy both templates and configure them for your environment.
1. Compose override — sets the path to your projects directory. Full absolute paths are required to avoid Docker-in-Docker volume mounting issues.
cp compose.override.yml.dist compose.override.ymlEdit compose.override.yml and replace the placeholder path:
services:
opencode:
volumes:
- /full/path/to/my/projects:/full/path/to/my/projects2. Environment file — sets local configuration variables.
cp .env.dist .envThe container runs as a non-root user matching your host UID/GID (detected automatically by the Makefile).
To pin a specific OpenCode version, set OPENCODE_VERSION in .env:
OPENCODE_VERSION=1.15.4Plugins extend the base image with additional tools and libraries. They are installed as separate optional layers — the base image stays lean by default.
Available plugins:
| Plugin | Adds |
|---|---|
midi |
fluidsynth, timidity, mido, pretty_midi, music21, and related Python audio libraries |
excel |
openpyxl for reading and writing .xlsx files |
browser |
Playwright Chromium (headless, MCP-controlled) — see Browser MCP below |
To enable plugins, set PLUGINS in your .env file (comma-separated):
PLUGINS=midi,excelThen build with:
make opencode-build-pluginsThe browser plugin adds @playwright/mcp@0.0.72 — a local MCP server that lets the browse agent control a headless Chromium browser.
Opt-in:
# .env
PLUGINS=browsermake opencode-build-plugins
make opencode-runRuntime behavior:
- Headless by default. The browser runs without a visible UI (
PLAYWRIGHT_HEADLESS=true). - Enabled when active. The MCP entry is
enabled: truewhen the browser plugin is active — activating the plugin is the opt-in. - Tool access is user-configurable. By default all agents can use
playwright_*tools. Restrict or scope access viaopencode.jsoncif needed. - Non-persistent state. No browser profile or cache is retained across container restarts (non-persistent by design).
- Standard outbound network. The container uses the same outbound network as the base image; no extra network restrictions are added for browser traffic.
Operations:
- Owner: Repo Maintainers
- Cadence: Monthly dependency/version review + immediate review on any OpenCode release,
@playwright/mcprelease, or CVE advisory. Review cadence: monthly.
Each plugin lives in its own subdirectory docker/plugins/<name>/ and may include up to three files:
| File | Purpose | Required |
|---|---|---|
<name>.dockerfile |
apt/system dependencies injected into the image | Yes |
<name>.package.json |
npm deps merged into .opencode/config/package.json at build time |
No |
<name>.opencode.jsonc |
MCP config fragment injected into opencode.jsonc at build time |
No |
Copy the template for the Dockerfile layer:
cp docker/plugins/plugin.dockerfile.dist docker/plugins/myplugin/myplugin.dockerfileThe template documents the available package managers (apt-get, pip) and their conventions for this base image.
If you already use opencode locally, you can reuse existing credentials and skip signing in again:
# macOS / Linux
cp ~/.local/share/opencode/auth.json .opencode/share/auth.jsonOtherwise, run make opencode-run, open http://localhost:4096, and authenticate in the UI.
Credentials are written automatically to .opencode/share/auth.json.
Note:
auth.jsonmay contain provider tokens. It is covered by.gitignoreand is not committed.
.opencode/config/ is mapped to the opencode config directory inside the container.
The committed template is .opencode/config/opencode.jsonc.base.dist. Copy it to get started:
cp .opencode/config/opencode.jsonc.base.dist .opencode/config/opencode.jsonc.baseEdit opencode.jsonc.base to customize behavior — providers, models, permissions, agents.
When you run make opencode-build-plugins, this file is used to generate opencode.jsonc.
Note: Do not edit
opencode.jsoncdirectly — it is a generated file and will be overwritten on the next plugin build. Editopencode.jsonc.baseinstead.
If you are not using plugins, you can create opencode.jsonc directly:
Both files are gitignored, so local customization does not affect others.
This repo uses .opencode/config/AGENTS.md to define default skill-loading behavior.
concise-preciseis loaded by default for user-facing responses.karpathy-guidelinesis loaded on top for non-trivial code changes.
This means response style stays concise by default, while coding workflow guidance is added when implementation tasks are complex.
The permission field controls which tool calls require approval.
The example above asks for confirmation on every bash command while allowing everything else.
To require approval for additional tools:
{
"permission": {
"bash": "ask",
"edit": "ask",
"write": "ask",
"*": "allow"
}
}See the permissions docs for all options.
The container mounts /var/run/docker.sock so opencode can run Docker commands on the host.
Socket permissions are handled automatically at startup by docker/entrypoint.sh:
it reads the socket owner GID and adds the opencode user to that group before dropping privileges.
Mounting the Docker socket grants the container full access to the host Docker daemon, so this setup does not provide meaningful isolation from the host.
| Host OS | Typical socket GID |
|---|---|
| macOS (Docker Desktop) | 0 (root) |
| Linux (Docker Engine) | 999 or varies |
This repo includes custom slash commands, a reusable skill system, and a PDF extraction tool.
See OpenCode commands, skills, and tools for the full command catalog and skill/tool reference.
If you only need the defensive baseline in a project:
/security-profile init
/security-profile refreshHappy agentic coding! Suggestions welcome!
{ "$schema": "https://opencode.ai/config.json", "autoupdate": false, "share": "disabled", "enabled_providers": ["github-copilot"], "permission": { "bash": "ask", "*": "allow" } }