DeepBlue Dynamics / Docs / Nemesis8 / Docker Environments

Docker Environments

How Nemesis8 builds and manages isolated agent containers.

Overview

Each Nemesis8 session runs in a Docker container built from a single Dockerfile maintained in the repo. The container bundles every AI provider CLI, a Python MCP venv, the Rust entry binary, and a standard set of system tools.

Containers are ephemeral by design — they start clean, receive your credentials and working directory via bind mount, run the agent, and are removed on exit. Persistent state (sessions, provider configs) lives on the host under ~/.nemesis8/.

What's in the image

Layer Contents
Basenode:24-slim — Node.js LTS on Debian Slim
System toolsgit, gh, curl, jq, ffmpeg, fzf, ripgrep, python3, bubblewrap, tini
Rust toolchainrustup stable — installed to /opt/rust/ (not shadowed by bind mounts)
Provider CLIsclaude-code, codex, gemini-cli, openclaw — installed globally via npm
Python MCP venv/opt/mcp-venv — all MCP tool dependencies pre-installed
MCP tools/opt/mcp-installed/ — Python MCP servers copied at build time
Entry binary/usr/local/bin/nemisis8-entry — built from source during image build

Building the image

# Default build — all four providers (claude, codex, gemini, openclaw)
nemisis8 build

# Slim build — only what you need
nemisis8 build --build-arg INSTALL_PROVIDERS=claude,codex

# Add optional extras (e.g. BAML)
nemisis8 build --build-arg INSTALL_EXTRAS=baml

# Force full rebuild
nemisis8 build --no-cache

The INSTALL_PROVIDERS build arg controls which provider CLIs are installed. Valid values: claude, codex, gemini, openclaw. Ollama sessions reuse the codex binary with an alternate OPENAI_BASE_URL — no separate install needed.

The Rust entry binary is compiled inside the Docker build. The first build takes a few minutes. Subsequent builds are fast because Docker caches all layers above the source COPY step.

Volume mounts

When a session starts, Nemesis8 bind-mounts several host paths into the container:

Host path Container path Purpose
~/.nemesis8/home/opt/codex-homeProvider home dir — auth files, provider configs
<working dir>/workspaceYour project — readable and writable by the agent
~/.nemesis8/sessions/opt/sessionsSession history and context persistence

The provider home dir (/opt/codex-home) is where the entry binary writes provider configs (MCP servers, system prompt, API keys) before handing off to the provider CLI. It survives container restarts because it's host-mounted.

The entry binary

nemisis8-entry is the Rust binary that runs inside the container as PID 1 (via tini). It is responsible for:

  1. Reading the provider definition from /opt/defaults/providers/<provider>.toml
  2. Writing the provider's config file (MCP servers, system prompt injection)
  3. Setting up environment variables (API keys, model selection, env overrides)
  4. Exec-ing into the provider CLI (e.g. claude, codex, gemini)
# The entry binary is called automatically — you don't invoke it directly.
# To see what it would do for a provider:
nemisis8 --provider claude --dry-run interactive

MCP tool servers

MCP (Model Context Protocol) servers are the tools available to the agent inside the container. They are Python scripts in /opt/mcp-installed/, pre-installed at image build time.

# List what's registered
nemisis8 mcp list

# Add a custom MCP server (persisted to provider config)
nemisis8 mcp add my-tool python3 /opt/mcp-installed/my_tool.py
SCREENSHOT — nemisis8 mcp list: table of registered MCP servers

MCP configs are written per-provider into the appropriate config format (TOML for Codex, JSON for Claude/Gemini) before each session start.

Cache invalidation

The Dockerfile injects a CACHE_BUST build arg above the COPY MCP/ and Rust build steps. Nemesis8 passes a timestamp as this arg so that a nemisis8 build always re-copies MCP sources and recompiles the entry binary — while keeping the slow provider install layers cached.

# Nemesis8 does this automatically:
docker build --build-arg CACHE_BUST=$(date +%s) ...

Provider CLI layers (the npm install step) are only invalidated when you explicitly pass --no-cache or when install-providers.sh changes. Normal nemisis8 build reuses those cached layers.