Run OpenCode, code-server, and a Playwright MCP browser sidecar with a shared filesystem and persistent data.
This setup is meant for people who want a browser-based AI coding agent and VS Code always available on a VPS or home server.
- OpenCode web UI served directly by the bundled
opencodebinary and code-server from one container - Playwright MCP sidecar for browser automation from the agent
- GitHub CLI available in the container for
ghcommands - Shared
/reposworkspace between both apps - Persistent OpenCode data, toolchains, and VS Code extensions
- Prewired MCP config for Context7 and Playwright
- Traefik-ready HTTPS routing for separate subdomains
- SSH key mounting for private Git access
- Preconfigured safeguards that block OpenCode from reading common secret files
You need:
- Docker and Docker Compose
- At least one LLM provider API key such as
ANTHROPIC_API_KEY,OPENAI_API_KEY, orGEMINI_API_KEY - A server or machine where the container can keep running
- Traefik with a
proxynetwork if you want the included domain-based routing - Optional:
GH_TOKENorGITHUB_TOKENif you wantghpre-authenticated
git clone <your-repo-url> opencode-docker
cd opencode-docker
cp .env.example .env
mkdir -p repos share agents config/mise config/opencode
docker compose up -dThen edit .env and set at least:
OPENCODE_SERVER_PASSWORDANTHROPIC_API_KEYor another supported provider keyOPENCODE_DOMAINandCODE_SERVER_DOMAINif you are using TraefikSSH_KEY_PATHif your SSH keys are not in~/.ssh
The included OpenCode config already points to the Playwright MCP sidecar at http://playwright-mcp:8931/mcp, so browser automation is available as soon as the stack starts.
The image now patches and builds OpenCode from source so the opencode binary embeds the upstream web UI bundle at compile time. That removes the runtime dependency on app.opencode.ai for the main web UI without needing a separate reverse proxy in the container.
After startup:
- OpenCode:
https://<OPENCODE_DOMAIN> - code-server:
https://<CODE_SERVER_DOMAIN>
| Variable | What it does |
|---|---|
OPENCODE_SERVER_PASSWORD |
Required password for the OpenCode web UI |
OPENCODE_SERVER_USERNAME |
Username for the OpenCode web UI |
CODE_SERVER_PASSWORD |
Optional separate password for code-server |
ANTHROPIC_API_KEY / OPENAI_API_KEY / GEMINI_API_KEY |
LLM provider credentials |
PUID / PGID |
Match container permissions to your host user |
OPENCODE_DOMAIN |
Domain for OpenCode |
CODE_SERVER_DOMAIN |
Domain for code-server |
CERT_RESOLVER |
Traefik certificate resolver |
SSH_KEY_PATH |
Host path to SSH keys |
GH_TOKEN / GITHUB_TOKEN |
Optional token for GitHub CLI and API access |
See .env.example for the full list.
./repos-> your repositories./config-> full~/.configpersistence including OpenCode config, installed skills, and mise config./share-> full~/.local/sharepersistence including OpenCode data, code-server data, and mise installs./agents-> compatibility config and skills for tools that use~/.agents
GitHub CLI auth also persists under ./config when you log in with gh auth login inside the container.
- This setup is intended for personal use, not multi-tenant hosting
- OpenCode is configured to deny reads for files such as
.env, SSH keys,*.pem, and*.key - SSH keys are mounted read-only and copied into the container at startup with the correct permissions
- Playwright MCP runs as a separate internal service and is only exposed on the private Compose network by default
- If both
OPENCODE_SERVER_PASSWORDandCODE_SERVER_PASSWORDare empty, code-server can run without auth
- The stack now includes a
playwright-mcpservice usingmcr.microsoft.com/playwright/mcp - OpenCode is preconfigured to connect to it through MCP at
http://playwright-mcp:8931/mcp - The current container setup uses headless Chromium with
--no-sandbox, matching the documented Docker usage for Playwright MCP - If you need a different image tag, set
PLAYWRIGHT_MCP_IMAGEin.env
If you do not use Traefik, remove the labels and networks sections from docker-compose.yml and access the mapped ports directly:
ports:
- "4096:4096"
- "8080:8080"4096 serves both the OpenCode API and the locally embedded web UI directly from the opencode process.
- Permission errors on mounted folders: set
PUIDandPGIDto match your host user - SSH access not working: verify
SSH_KEY_PATHand confirm the files are readable by that user - code-server auth issue: set
OPENCODE_SERVER_PASSWORDeven if you leaveCODE_SERVER_PASSWORDempty - Browser actions failing unexpectedly: check
docker compose logs playwright-mcpand confirm the sidecar is healthy - Toolchains reinstalling or changing: check
config/mise/config.tomland restart the container - GitHub CLI not authenticated: set
GH_TOKENorGITHUB_TOKEN, or rungh auth loginin the container - OpenCode page still tries to reach the internet: rebuild the image so the patched OpenCode binary with embedded web assets is actually installed
DockerfilepinsGH_VERSION,CODE_SERVER_VERSION, andOPENCODE_VERSION, so image builds stay reproducible instead of silently pullinglatestrenovate.jsonteaches Renovate to watchcli/cli,coder/code-server, andanomalyco/opencodereleases and open PRs when any pinned version can be bumped- Enable the Renovate app or runner for this repository to start receiving update PRs automatically