This service runs opencode serve for Sim. It backs the optional OpenCode workflow block and can also be queried by internal tooling against one or more cloned repositories.
- HTTP service on
http://opencode:4096inside Docker, with an optional published host port for local development - HTTP basic auth via
OPENCODE_SERVER_USERNAMEandOPENCODE_SERVER_PASSWORD - Persistent OpenCode storage in
~/.local/share/opencode - Optional multi-repo sync into
/app/repos - Global read-only OpenCode permissions
At minimum, set:
OPENCODE_REPOSITORY_ROOT=/app/repos
OPENCODE_SERVER_USERNAME=opencode
OPENCODE_SERVER_PASSWORD=change-me
OPENCODE_REPOS=https://github.com/octocat/Hello-World.git
GEMINI_API_KEY=your-gemini-keyNotes:
- The UI block is intentionally hidden until
NEXT_PUBLIC_OPENCODE_ENABLED=trueis set on the Sim app. OPENCODE_REPOSITORY_ROOTdefaults to/app/reposand must match the path Sim uses when it resolves repository directories.OPENCODE_SERVER_USERNAMEdefaults toopencodein the optional compose overlays if omitted.docker-compose.opencode.local.ymldefaultsOPENCODE_SERVER_PASSWORDtodev-opencode-password, but setting it explicitly is safer and avoids app/container credential drift.docker-compose.opencode.ymlrequiresOPENCODE_SERVER_PASSWORDto be provided from the environment beforedocker composestarts.- OpenCode needs at least one provider key to answer prompts:
OPENAI_API_KEYANTHROPIC_API_KEYGEMINI_API_KEYGOOGLE_GENERATIVE_AI_API_KEY
- In the optional compose overlays,
GOOGLE_GENERATIVE_AI_API_KEYis automatically derived fromGEMINI_API_KEYif not set explicitly.
Set OPENCODE_REPOS to a comma-separated list of HTTPS repository URLs.
OPENCODE_REPOS=https://github.com/org/ui-components,https://github.com/org/design-tokensAzure Repos over HTTPS is also supported. Example:
OPENCODE_REPOS=https://dev.azure.com/org/project/_git/repoEach repository is cloned into ${OPENCODE_REPOSITORY_ROOT:-/app/repos}/<repo-name>. On restart, existing clones are updated with git pull --ff-only. A background cron sync retries every 15 minutes.
For private repositories, provide HTTPS credentials with one of these options:
GIT_USERNAMEandGIT_TOKENGITHUB_TOKENfor GitHub HTTPS access
For Azure Repos, use GIT_USERNAME plus an Azure DevOps PAT in GIT_TOKEN. The container uses non-interactive GIT_ASKPASS, so it will not stop to ask for a password in the terminal during clone or pull.
If a clone or pull fails, the service logs the error and continues syncing the remaining repositories.
If you run next dev on the host instead of inside Docker, the app must reach OpenCode through the published host port.
Add this to apps/sim/.env:
NEXT_PUBLIC_OPENCODE_ENABLED=true
OPENCODE_BASE_URL=http://127.0.0.1:4096
OPENCODE_REPOSITORY_ROOT=/app/repos
OPENCODE_SERVER_USERNAME=opencode
OPENCODE_SERVER_PASSWORD=change-meThen load the same environment into your shell before starting the OpenCode container:
set -a
source apps/sim/.env
set +a
docker compose -f docker-compose.local.yml -f docker-compose.opencode.local.yml up -d --build opencodeThis matters because apps/sim/.env configures the host-side Next.js app, but docker compose only sees variables present in the shell environment.
If Sim itself also runs in Docker, use the same local overlay without targeting just opencode:
docker compose -f docker-compose.local.yml -f docker-compose.opencode.local.yml up -d --buildVerification differs slightly between local and production-style compose.
docker-compose.opencode.local.yml publishes OPENCODE_PORT to the host, so this should work from the host:
curl -u "$OPENCODE_SERVER_USERNAME:$OPENCODE_SERVER_PASSWORD" \
http://127.0.0.1:${OPENCODE_PORT:-4096}/global/healthCreate a session from the host:
curl -u "$OPENCODE_SERVER_USERNAME:$OPENCODE_SERVER_PASSWORD" \
-H "Content-Type: application/json" \
-d '{"title":"test"}' \
http://127.0.0.1:${OPENCODE_PORT:-4096}/sessionProduction should use the base compose plus the OpenCode overlay:
docker compose -f docker-compose.prod.yml -f docker-compose.opencode.yml up -d --buildThe overlay injects NEXT_PUBLIC_OPENCODE_ENABLED, OPENCODE_BASE_URL, OPENCODE_PORT, OPENCODE_REPOSITORY_ROOT, OPENCODE_SERVER_USERNAME, and OPENCODE_SERVER_PASSWORD into simstudio, so the app can authenticate against the internal OpenCode server without changing docker-compose.prod.yml.
If you prefer to run OpenCode in separate infrastructure, skip the overlay and set the same app variables directly on the Sim deployment. The external OpenCode runtime must expose project worktrees under the same OPENCODE_REPOSITORY_ROOT that Sim is configured to use.
OpenCode stays internal to the Docker network, so verify from another container:
docker compose -f docker-compose.prod.yml -f docker-compose.opencode.yml exec simstudio \
curl -u "$OPENCODE_SERVER_USERNAME:$OPENCODE_SERVER_PASSWORD" \
http://opencode:${OPENCODE_PORT:-4096}/global/healthCreate a session:
docker compose -f docker-compose.prod.yml -f docker-compose.opencode.yml exec simstudio \
curl -u "$OPENCODE_SERVER_USERNAME:$OPENCODE_SERVER_PASSWORD" \
-H "Content-Type: application/json" \
-d '{"title":"test"}' \
http://opencode:${OPENCODE_PORT:-4096}/sessionUseful runtime checks:
docker logs --tail 100 sim-opencode-1
docker exec sim-opencode-1 env | grep OPENCODE
docker exec sim-opencode-1 env | grep -E 'OPENAI|ANTHROPIC|GEMINI|GOOGLE_GENERATIVE'Expected signals:
opencode server listening on http://0.0.0.0:4096[opencode-sync] Updated <repo-name>or clone logs- the same username/password and provider env vars you expect the app to use
Before accepting a deployment, validate the read-only permission config with a real prompt against a cloned repository. The check should confirm that OpenCode can still read files while edit, bash, and web-capable tools remain blocked. If the wildcard rule prevents normal reads, remove permission."*": "deny" and keep the explicit tool denies as the fallback.
Each cloned repository can keep its own AGENTS.md and opencode.json at the repo root. OpenCode will use those when a future client targets that repository directory.
The SDK also supports injecting extra per-session context without triggering a reply by calling session.prompt with noReply: true. The current Sim block can evolve to use this for dynamic runtime instructions on top of repository-local configuration.
- Session retention is not managed yet. OpenCode data persists until the
opencode_datavolume is pruned. - The compose overlays are convenience wrappers. The app can also target any compatible external OpenCode deployment through
OPENCODE_BASE_URL, the same server credentials, and the sameOPENCODE_REPOSITORY_ROOT.