Allow opencode to run Docker images via a dind sidecar

opencode now drives a dedicated Docker-in-Docker container instead of the
host daemon, so it can build/run images and bring up compose stacks from
within its own container.

Notes:
- dind runs in PRIVILEGED mode, this required for a nested docker daemon.
- opencode itselfs remains unprivileged
- opencode talks to dind over TCP (DOCKER_HOST=tcp://dind:2375)
  Consequence: services started in dind are reachable at dind:<port>, not
  localhost.
- The workspace needs to be bind-mounted at the same absolute path in both containers
  so compose bind-mounts resolve inside the dind daemon.
- /var/lib/docker is persisted on a mounted volume to keep images warm across
restarts.
- uses the rpio registry mirror for quicker pulls
This commit is contained in:
2026-07-06 12:51:32 +02:00
parent 48caec8669
commit e6e5eadcf8
2 changed files with 38 additions and 1 deletions

View File

@@ -119,6 +119,19 @@ no `ports:` publishing needed.
Note: Bring the opencode stack up first, so `app-opencode_default` exists before adding to your app. Note: Bring the opencode stack up first, so `app-opencode_default` exists before adding to your app.
### Executing docker commands from opencode
The stack runs a dedicated dind (docker in docker) container, and opencode can drive it as a client (`DOCKER_HOST=tcp://dind:2375`). So from inside opencode you can build images, run containers, and `docker compose up` a project's stack. The agent can spin up and test the services it's working on.
A few things that differ from a normal host, because the daemon lives in dind:
- Containers and published ports you start run on the **dind** daemon. Reach a running service at `dind:<port>`, **not** `localhost:<port>`. opencode does not
share dind's network namespace, so `localhost` won't reach the stack.
- The repo needs to be mounted at the same absolute path (`/workspace/repo`) in both the opencode and dind containers, so compose bind-mounts resolve correctly. Work on the checkout there; edits elsewhere won't be visible to the containers dind runs.
- The dind image store is cached in `./data/dind-cache`, so pulled images stay warm across restarts.
- To access the services from the host machine, you will need to connect to the dind container or publish the necessary ports from the dind container to your host.
> Note: Opencode itself doesn't run in a privileged container so it can't do privileged actions directly, but it can use the docker daemon in dind to do the same things. Because dind is privileged and in the host user namespace, a container started inside dind can mount the host's raw disk devices and reach the **entire** host filesystem, not only what you shared. This needs opencode to actively do it, but it's a well-known technique, not a theoretical edge case.
## Reference ## Reference
### `opencode.json` keys ### `opencode.json` keys

View File

@@ -1,6 +1,30 @@
services: services:
opencode: opencode:
image: redpencil/opencode:0.0.1 image: redpencil/opencode:feature-with-docker
volumes: volumes:
- ./data/opencode/share:/root/.local/share/opencode - ./data/opencode/share:/root/.local/share/opencode
- ./config/opencode/opencode.json:/root/.config/opencode/opencode.json - ./config/opencode/opencode.json:/root/.config/opencode/opencode.json
# - ./config/opencode:/root/.config/opencode
depends_on:
dind:
condition: service_healthy
environment:
DOCKER_HOST: "tcp://dind:2375"
dind:
image: docker:29.6-dind
privileged: true
# userns mode only required if you use user namespaces
userns_mode: "host"
environment:
DOCKER_TLS_CERTDIR: ""
volumes:
- ./data/dind-cache:/var/lib/docker
command:
- "--registry-mirror=https://docker-registry-mirror.redpencil.io"
healthcheck:
test: ["CMD", "docker", "info"]
interval: 5s
timeout: 3s
retries: 20
start_period: 10s