Security-focused Docker container setup for running OpenCode.
Design goals:
- Restrict OpenCode's filesystem access by only mounting specific files/folders
- Limit what files OpenCode can view and edit
- Allow running Docker from inside the container to execute code with specific dependencies
This is NOT the OpenCode source code - it's a containerized environment for safely using OpenCode on development projects.
# Using the build script (recommended)
./build.sh
# Or directly with docker
docker build -t opencode-fk -f Dockerfile .Run from your project directory:
# Using the run script (recommended)
./path/to/opencode.sh
# Or directly with docker
docker run -it --rm \
-v "${PWD}:${PWD}" \
-v "${HOME}/.config/opencode/":/home/dev/.config/opencode/ \
-v /var/run/docker.sock:/var/run/docker.sock \
-w "${PWD}" \
--network host \
opencode-fkdocker run --rm -it \
-v "$(pwd):$(pwd)" \
-w "$(pwd)" \
opencode-fk <command>Host Machine Container
┌─────────────────┐ ┌─────────────────┐
│ Project Code │◄──mount───►│ Project Dir │
│ (current dir) │ │ (same path) │
├─────────────────┤ ├─────────────────┤
│ Docker Daemon │◄──socket─►│ docker client │
│ │ mount │ (run tests) │
└─────────────────┘ └─────────────────┘
The container includes the following environment variables:
OPENCODE_VERSION- The OpenCode version baked into the container imageUID/GID- User/group IDs for the dev user
Only explicitly mounted directories are accessible to OpenCode. Key mounts in opencode.sh:
- Project directory (current working directory, mounted at same path)
~/.config/opencode/(OpenCode settings)~/.ssh/configand sockets (SSH access)/var/run/docker.sock(Docker-in-Docker for dependency execution)
This project does not contain application source code to test. To verify the container works:
# Test container builds successfully
./build.sh
# Test container runs and OpenCode is available
docker run --rm -it opencode-fk opencode --versionFollow these conventions:
- Use strict mode:
set -euo pipefail - Use lowercase with underscores for variables:
project_dir,docker_gid - Use uppercase for constants:
DEFAULT_PORT=8080 - Quote all variable expansions:
"${VAR}"not$VAR - Use
$(..)not backticks for command substitution - Handle command failures:
DOCKER_GID=$(getent group docker | cut -d: -f3) || DOCKER_GID=999 - Use
[[ ]]for tests, not[ ] - Use case statements for pattern matching
#!/usr/bin/env bash
set -euo pipefail
SCRIPT_DIR="$( cd -- "$( dirname -- "${BASH_SOURCE[0]}" )" &> /dev/null && pwd )"
case "${ARCH}" in
amd64|x86_64) ARCH="x64" ;;
arm64|aarch64) ARCH="arm64" ;;
*) echo "Unsupported architecture" >&2; exit 1 ;;
esacFollow Docker best practices:
- Use specific tags:
debian:13.3-slimnotdebian:latest - Put least-changing layers first, most-changing last
- Combine related operations to reduce layers
- Run as non-root user when possible
- Use
--no-install-recommendsand clean up caches
RUN apt-get update && apt-get install -y --no-install-recommends \
curl git jq && rm -rf /var/lib/apt/lists/*
ARG USERNAME=dev
RUN groupadd -g ${GID:-1000} ${USERNAME} && \
useradd -m -u ${UID:-1000} -g ${GID:-1000} -s /bin/bash ${USERNAME}
USER ${USERNAME}
HEALTHCHECK --interval=30s --timeout=10s --start-period=5s --retries=3 \
CMD opencode --version || exit 1- Scripts should be executable:
chmod +x build.sh opencode.sh - Use
#!shebang with/usr/bin/envfor portability
- Explain why, not what
- Use comments to document non-obvious decisions
- Use imperative mood: "Add feature" not "Added feature"
- First line under 50 characters
Use the built-in update command:
./path/to/opencode.sh updateThis will:
- Check GitHub for the latest version
- Show a banner with the new version number
- Wait for keypress to continue
- Build a new container with the latest version
- Update the version file (
.opencode-version)
The update command does not start the container—it only updates the Docker image.
The current version is displayed on startup and is also available inside the container as the OPENCODE_VERSION environment variable.
The version file (.opencode-version) is stored in the same directory as opencode.sh, not in the project directory. This allows using different OpenCode versions for different projects by placing opencode.sh in each project.
On regular runs, the script:
- Reads stored version from
.opencode-version(next to opencode.sh) - Checks GitHub for newer version (1 second timeout, non-blocking)
- If newer version available, shows banner and waits for keypress before continuing
- Runs container with stored version (uses cached image)
- Displays current version on startup
The version is persisted in .opencode-version and also baked into the container as OPENCODE_VERSION environment variable.
Edit opencode.sh and add to the docker run command:
-v "${HOME}/path/to/dir":/home/dev/path/to/dirEnsure your user is in the docker group or use --group-add ${DOCKER_GID} as shown in opencode.sh.
The container mounts /tmp/.X11-unix for X11. Ensure X server is running on host.
Verify ~/.ssh/config exists on host and is mounted in opencode.sh.