My vision a lightweight, permission-headache-free Docker setup for running OpenCode, uv, and untrusted Python code without risking your host OS.
When contributing to unfamiliar open-source projects or letting AI coding agents (like OpenCode) run terminal commands, there's always a slight hesitation.
What if a build script touches my system Python, or a rogue command wipes host files?
To solve this, I built saferun a zero-trust, disposable Docker sandbox designed specifically for Python developers and AI agent workflows on macOS and Linux.
Here’s how it works, the permission nightmares I had to solve, and how you can set it up in under two minutes.
The Goal
I wanted a workspace that gave me:
-
Absolute Isolation: Runtime scripts,
pytest,ruff, and AI agent commands execute strictly inside a disposable Linux container. - Seamless IDE Integration: Files edited inside PyCharm or VS Code on the host machine sync instantly with the container.
-
Zero Permission Headaches: Any files generated inside the sandbox belong to my host user account—not
root. -
Persistent Speed: Package downloads cached permanently via
uvso environment startup stays millisecond-fast. - Isolated Credentials: Global SSH and Git keys remain safely on the host machine.
Solving the "Non-Root" Docker Nightmare
The hardest part of containerized dev environments is file ownership. If you run Docker as root, any file your AI agent generates belongs to root, locking you out on your host machine.
If you pass your local user ID (-u "$(id -u):$(id -g)"), Docker mounts non-existent directories as root:root, causing Permission Denied crashes when tools like uv try to write to cache folders.
saferun solves this inside the base Dockerfile by pre-creating cache directories and granting open write permissions upfront:
FROM python:3.12-slim
# Install curl (needed to install OpenCode)
RUN apt-get update && apt-get install -y --no-install-recommends \
curl \
&& rm -rf /var/lib/apt/lists/*
# Install uv globally
RUN pip install --no-cache-dir uv
# Install OpenCode and dynamically find/copy its binary to /usr/local/bin
RUN curl -fsSL https://opencode.ai/install | bash && \
find /root -name "opencode" -type f -exec cp {} /usr/local/bin/ \;
# Pre-create paths and fix permissions for dynamic non-root UIDs
RUN mkdir -p /uv-cache /app-data /tmp/.config && chmod 777 /uv-cache /app-data /tmp/.config
# Fix the "I have no name!" prompt for dynamic UIDs
RUN echo 'export PS1="Docker Terminal:\w\$ "' >> /etc/bash.bashrc
WORKDIR /app
CMD ["bash"]
The One-Liner Alias:
Once the base image is built, everything ties together into a single terminal alias added to your ~/.zshrc or ~/.bashrc:
alias saferun='docker run --rm -it -u "$(id -u):$(id -g)" -e HOME=/tmp -e UV_CACHE_DIR=/uv-cache -e XDG_DATA_HOME=/app-data -v "$(pwd):/app" -v uv-cache:/uv-cache -v ~/.config/opencode:/tmp/.config/opencode -v ~/.local/share/opencode:/app-data/opencode -w /app saferun-base'
What this does under the hood:
--rm: Destroys the container instantly when you type exit.-v "$(pwd):/app": Live-mounts your current working directory.-v uv-cache:/uv-cache: Mounts a dedicated Docker volume for package caching.-v ~/.config/opencode...: Maps your host OpenCode login and agents.md settings so you don't have to re-authenticate every session.
Workflow in Action:
Clone any repository locally.
Open your terminal at the project root and type
saferun.You are now inside an isolated Linux shell. Run untrusted code, let
uvhandle dependencies (uv run pytest), or askopencodeto refactor code safely.
Type exit. The container vanishes, while all code edits remain safely saved on your machine.
Check out the Repo:
The full step-by-step setup guide and Dockerfile details are open-source on GitHub.
👉 github.com/henderson-01/saferun
If you try it out or have ideas to improve it, I'd love to hear your feedback in the comments!
This article was originally published by DEV Community and written by Mr Henderson.
Read original article on DEV Community