Basic OpenCode Sandboxing with Docker

I’ve been playing with OpenCode as a way to test different models, play with self-hosted models and to avoid reliance on big tech provided systems. With these kinds of tools, I’m not keen on giving it free access to all my files. Therefore, I came up with a simple little docker based setup so that OpenCode’s access is limited somewhat.

Note: I am not a security expert and this kind of setup does not assure any kind of absolute sandboxing or security.

Here’s the Dockerfile:

FROM node:24-trixie

RUN npm install -g opencode-ai

RUN mkdir -p /home/node/.local/share/opencode \
             /home/node/.local/state/opencode \
             /home/node/.cache/opencode \
    && chown -R node:node /home/node

ENTRYPOINT ["opencode"]

This installs OpenCode within a Debian Trixie-based image with Node.js installed.

Build that from the same directory using docker build -t local:opencode . and then create the following bash script as opencode, located somewhere in your $PATH:

#!/usr/bin/env bash

set -euo pipefail

CWD="$(pwd)"

# Check if CWD is under /home/ and at least 3 levels deep (e.g. /home/user/project/subdir)
is_valid_path() {
    local path="$1"
    # Must start with /home/
    [[ "$path" == /home/* ]] || return 1
    # Strip /home/ prefix and count remaining components
    local relative="${path#/home/}"
    # Count slashes to determine depth (need at least 2 more levels after /home/)
    local depth
    depth=$(echo "$relative" | tr -cd '/' | wc -c)
    [[ "$depth" -ge 2 ]] || return 1
}

if is_valid_path "$CWD"; then
    CONTAINER_WORKDIR="/app${CWD}"
else
    # Construct a fallback path at least 3 levels deep under /home/
    USER_NAME="${USER:-node}"
    PROJECT_NAME="$(basename "$CWD")"
    TIMESTAMP="$(date '+%Y_%m_%d-%H%M%S')"
    FALLBACK="/home/${USER_NAME}/Projects/scratch/opencode-${TIMESTAMP}"
    CONTAINER_WORKDIR="/app${FALLBACK}"
    CWD="${FALLBACK}"

    echo ""
    echo -e "\033[31mNot in a safe project directory.\033[0m"
    echo "Starting OpenCode in a fallback temp directory: ${FALLBACK}"
    mkdir -p "${FALLBACK}"
    echo -n "Loading"
    for i in $(seq 1 50); do
        echo -n "."
        sleep 0.06
    done
    echo ""
fi

exec docker run --rm -it \
    --user "$(id -u):$(id -g)" \
    -v "$HOME/.config/opencode:/home/node/.config/opencode" \
    -v "$HOME/.cache/opencode:/home/node/.cache/opencode" \
    -v "$HOME/.local/share/opencode:/home/node/.local/share/opencode" \
    -v "$HOME/.local/state/opencode:/home/node/.local/state/opencode" \
    -v "${CWD}:${CONTAINER_WORKDIR}" \
    -w "${CONTAINER_WORKDIR}" \
    -p "127.0.0.1:4040:4040" \
    --security-opt=no-new-privileges \
    --cap-drop all \
    local:opencode "--port" "4040" "--hostname" "0.0.0.0" "$@"

This script runs the container, running OpenCode as the same user/group ID as the host user. All the OpenCode config/cache/state directories are passed through to retain sessions/login/state between sessions. Then the current working directory is mapped into the container at /app$(pwd). I include the /app prefix to contain working data to a specific place inside the container.

UPDATE - 2026-05-13
I’ve added some checks to the script to ensure that OpenCode is being opened in a likely project dir, nested with the user’s home dir. This is to help avoid accidentally opening it in a folder which will provide a wider scope of user files (like the entire home directory). If opened outside such a folder, it creates and uses a ~/Projects/scratch/opencode-DATETIME folder instead. Some docker options have been added/tweaked to improve sandboxing.

Port 4040 is passed through between host and container, which is set for OpenCode on the next line, along with a 0.0.0.0 hostname. These are to allow the web UI to be accessed via http://localhost:4040 on the host. Lastly, it runs the container using the local:opencode tag we built it with, setting those web server UI options and then it passes any extra options which the user provided when running the bash script.

Make the script executable with chmod +x opencode. Then, with the script in your $PATH somewhere, you should be able to run opencode to start the docker-based OpenCode in your current working directory. In the bottom left corner you should see the path starting with /app/

To re-iterate, this does not assure security in any way, and there may be better options elsewhere such as in this thread but for me I wanted a setup which was simple, using the tools I’m familiar with, which should cover my greatest concern (unrestricted access to my files). Your concerns & considerations may differ.

UPDATE - 2026-05-13
Michael Lynch provided a good suggestion of using rootless Podman to improve security further, which is a great idea if you’re not married to traditional docker.

There may also be limitations/complications/side-effects with this kind of setup, I’m still in the early stages of testing it.

I did try to initially use “Docker Sandboxes”, but I abandoned this route when it required me to accept non-open terms when following their usage guide. From what I can see at a glance, this is a proprietary service/offering.