In my previous post, I described how to create a separate OS user to run Claude Code. This creates some nice separation between the AI agent account and your main user account so you’re not leaving your front door open for the AI agent, and you can keep it under control.

Recently, I found an even better approach: using Docker for this purpose. This gives you even stronger isolation.

I’ve started using OpenCode which, if you like Claude Code, can easily be configured to work with Claude Code as a passthrough.

Dockerfile

Let’s start by creating a Docker image. This allows you to control what utilities are installed in the OS, which user is used, and other basics.

FROM ghcr.io/anomalyco/opencode:latest

USER root

# Install additional tools
RUN apk add --no-cache \
    curl \
    git \
    bash \
    openjdk25 \
    maven \
    xdg-utils \
    xclip \
    wl-clipboard
    ca-certificates \
    unzip

# Create 'piotr' user with UID=1000 to match the host user
RUN addgroup -g 1000 piotr && \
    adduser -D -u 1000 -G piotr -h /home/piotr -s /bin/bash piotr

USER piotr

# Set git config for OpenCode to recognize user
RUN git config --global user.name "Piotr Nowicki" && \
    git config --global user.email "[email protected]"

ENV JAVA_HOME=/usr/lib/jvm/java-25-openjdk
ENV PATH="${JAVA_HOME}/bin:${PATH}"

WORKDIR /workspace

You can easily extend the utils list to add whatever you need.
I also use my host OS user inside the container to avoid permission issues with the files the AI will work on. I’ll be working on the same files using my IDE on the host, so I want full transparency here.

Save it as Dockerfile and run: docker build . -t opencode-custom

Docker compose

Now let’s move to the Docker Compose file that will use the image we just built:

services:
  opencode:
    build: .
    image: opencode-custom:latest
    container_name: opencode-custom

    # Needed to be able to type input as in regular TTY
    stdin_open: true
    tty: true

    volumes:
      # Projects to work on
      - /home/piotr/IdeaProjects/my-nice-project:/workspace/my-nice-project:rw

      # OpenCode
      - ./data/home:/home/opencode:rw
      - ./config:/config:rw

    working_dir: /workspace

    environment:
      - HOME=/home/opencode
      - OPENCODE_API_KEY=${OPENCODE_API_KEY:-}          # To be used with OpenCode Zen
      - OPENCODE_CONFIG=/config/custom-config.json

    networks:
      - opencode-net

    security_opt:
      - no-new-privileges:true
    cap_drop:
      - ALL
    cap_add:
      - CHOWN
      - SETUID
      - SETGID
    read_only: false

    restart: unless-stopped

networks:
  opencode-net:
    driver: bridge

Save it as docker-compose.yml next to Dockerfile.
Then run: docker compose up -d
Connect to it using: docker exec -it opencode-custom opencode

The Docker Compose setup grants the minimum required permissions to the container and only mounts the projects you actually want it to have access to.

This setup gives me confidence and freedom to let the agent do many things automatically without worrying it will suddenly go into Skynet mode on me ;-)