Each container is locked to a repository-branch pair. No confusion. Safe parallel work.
Safe parallel work with multiple AI coding agents. Run Claude Code, Codex, Amp CLI, Gemini CLI, Kilo CLI, Pi, and other tools in isolated containers without branch conflicts, file interference, or environmental headaches.
AI coding agents work best when given full command execution (YOLO mode). But running them locally is risky:
- Branch conflicts - Agent changes on one branch interfere with your local checkout
- Test conflicts - Running tests simultaneously causes failures and race conditions
- Environment pollution - Dependencies and state accumulate, causing hidden bugs
- Context switching - Switching branches while an agent is working loses progress
md gives each AI agent a complete, isolated container with a full git clone. You can:
- Run agents on multiple branches simultaneously
- Switch local branches without affecting running agents
- Run tests in parallel without conflicts
- Keep your local checkout clean
- Delete containers cleanly when done
- Access your frontend dev server over Tailscale with HTTPS!
- Share your host's USB port for Android debugging
- Let the coding agent control an Android Emulator and see it over VNC
# Start container for your current branch; this automatically ssh in.
git checkout -b wip origin/main
md start
# You are now inside the container
cd ~/src/<repo-name>
# Run the coding harness of your choice, a bash alias will automatically start it in YOLO mode:
claude
# Exit from the ssh session into the container (or use a separate terminal)
exit
# Check pending changes
md diff
# Pull changes back when done
md pull
# Clean up the container
md killgo install github.com/caic-xyz/md/cmd/md@latestRecommended: Also install git-maruel for the git squash and git rb helpers.
See HARNESSES.md for coding harness (claude code, codex, etc) specifics.
Each container is named md-<repo-name>-<branch-name> with:
- Isolated git clone -
~/src/<repo-name>inside the container is a git clone of your local repository. It tracks branchbasewhich matches your local branch. This is useful for commit-happy agents like Codex to track pending changes. - User-mapped permissions - Container runs as your local user ID for proper file permissions
- SSH access - Connect via
ssh md-<repo>-<branch> - Remote GUI (VNC) - Optional full desktop environment (via
-display) accessible via VNC on a dynamic port - Remote network (Tailscale) - Optional full access to your tailnet (via
-tailscale) - Local USB debugging - Optional USB debugging (via
-usb), especially for Android development - Minimal overhead - Only sshd runs by default; no unnecessary background services
- TypeScript, Go, Rust, Node.js, Python development environments
- Neovim editor
- Android SDK and ADB on x64
- And more! See rsc/root/setup/ and rsc/home/user/setup/.
Agent configurations and credentials are automatically mounted:
- Agent configurations:
~/.amp,~/.claude,~/.codex,~/.gemini,~/.kilocode,~/.kimi,~/.pi,~/.qwen,~/.config/agents,~/.config/amp,~/.config/goose,~/.config/opencode,~/.local/share/amp,~/.local/share/goose,~/.local/share/opencode,~/.local/state/opencode - Android ADB keys:
~/.android
md start and md run automatically bake your local build-tool caches into the md-user Docker image at
build time. This means the container starts with warm caches, skipping the slow cold downloads that would
otherwise happen on every fresh container.
Enabled by default (host directory must exist; silently skipped otherwise):
| Name | Host path | Container path |
|---|---|---|
bun |
~/.bun/install/cache |
/home/user/.bun/install/cache |
cargo |
~/.cargo/registry, ~/.cargo/git |
/home/user/.cargo/{registry,git} |
go-mod |
~/go/pkg/mod |
/home/user/go/pkg/mod |
gradle |
~/.gradle/caches, ~/.gradle/wrapper/dists |
/home/user/.gradle/{caches,wrapper/dists} |
maven |
~/.m2/repository |
/home/user/.m2/repository |
npm |
~/.npm |
/home/user/.npm |
pip |
~/.cache/pip |
/home/user/.cache/pip |
pnpm |
~/.local/share/pnpm/store |
/home/user/.local/share/pnpm/store |
uv |
~/.cache/uv |
/home/user/.cache/uv |
The md-user image is only rebuilt when the set of available caches changes, the base image updates, or
the build context changes. Cache contents are snapshotted at build time; they are not kept in sync after
that.
Opt out of specific caches:
md start -no-cache go-mod -no-cache cargo # skip specific caches
md start -no-caches # disable all caches
md start -no-caches -cache go-mod # only go-modCustom cache directories (any host:container[:ro] path pair):
md start -cache /path/to/my/cache:/home/user/.mycacheEnvironment variables can be passed via:
.envfile in your repository (auto-mapped)~/.config/md/envon your local machine (applies to all containers)
For example:
# ~/.config/md/env
ANTHROPIC_API_KEY=your_key
OPENAI_API_KEY=your_keyThe container doesn't have access to your GitHub credentials. To enable git credentials and access to GitHub (e.g. to create PRs or issues), authenticate inside the container via:
gh auth login| Command | Purpose |
|---|---|
md start |
Create and start a container for the current branch |
md start -display |
Start container with X11/VNC desktop environment enabled |
md start -tailscale |
Start container with Tailscale networking for remote SSH access |
md start -usb |
Start container with USB device passthrough (for ADB, etc.) |
md start -no-cache <name> |
Exclude a default cache (repeatable); e.g. -no-cache go-mod |
md start -no-caches |
Disable all default caches |
md start -cache host:container |
Add a custom cache directory |
md run <cmd> |
Start a temporary container, run a command, then clean up |
md list |
List all md containers |
ssh md-<repo>-<branch> |
Access the container via SSH |
md vnc |
Open VNC connection to the container |
md diff |
Show changes (runs git diff base). Arguments are passed through, e.g. md diff --stat |
md pull |
Pull changes from container back to local branch |
md push |
Push local changes to the container |
md kill |
Stop and remove the container |
md build-image |
Build the base Docker image locally as md-local |
md start -image md-local |
Start a container using the locally built base image |
Each container can include a full XFCE4 desktop environment. It must be enabled at startup:
md start -display
md vncmd vnc opens the VNC connection in your default VNC client.
Recommended VNC clients by OS:
- Windows: RealVNC Viewer, TightVNC, or UltraVNC
- macOS: Built-in VNC support or RealVNC Viewer
- Linux:
tigervnc-viewer,vinagre, orvncviewercommand-line tools
The DISPLAY environment variable is automatically set in SSH sessions, so X11 applications launched from SSH will appear on the VNC desktop.
Made with ❤️ by Marc-Antoine Ruel. Contributions are very appreciated! Thanks in advance! 🙏