Docker Images
Choosing and building the image an agent runs in — what the agent images contain, per-agent CLIs and config paths, building with heretic-cli image, custom bases, and matching container UIDs
Every agent runs in a container, and the profile's image: field decides which one:
# ~/.heretic/agents/claude.yaml
image: giglabo/claude-heretic:latest
The image is pulled automatically on heretic-cli run <agent> when it is not present locally. A failed pull stops the run with Failed to pull image <image>: ….
Choosing an image
| Option | When |
|---|---|
giglabo/claude-heretic:latest | the default Claude Code image, and what heretic-cli init suggests |
heretic-cli image build … | you need extra toolchains, a different agent CLI, or a specific UID |
| your own Dockerfile | you already maintain a dev image and want to add an agent CLI to it |
heretic-cli image build # → heretic-agent:latest (Claude)
heretic-cli image build --agent copilot # → heretic-agent-copilot:latest
heretic-cli image build --agent all --combined -n heretic-agent -t all
Full flag reference: heretic-cli image.
What a heretic agent image contains
Built by heretic-cli image build, on node:22-bookworm-slim unless you pass --base:
| Component | Purpose |
|---|---|
| Node.js 22 | runtime for the agent CLIs, which are npm packages |
| npm, plus yarn and pnpm via corepack | JavaScript package managers |
| git | repository work inside the container |
gh (GitHub CLI) | PRs, issues and reviews from inside the agent — on by default, --no-github-cli removes it |
| curl, jq | required by the build-sidecar client |
| openssh-client | required by the SSH backend |
| vim, unzip, gnupg, xz-utils, ca-certificates | everyday basics |
/opt/sidecar/{sidecar-exec,ssh-exec} plus a writable wrappers directory on PATH | tool-execution backends |
/entrypoint.sh baked as ENTRYPOINT | root mode, wrapper generation, interactive and workflow modes |
non-root agent user, uid/gid 1000 | matches the common host uid so bind mounts stay writable |
Optional toolchains — Python, Go, Java, Rust, Docker CLI — are not included unless you ask for them (--with-python, --with-go, --with-java, --with-rust, --with-docker, or --with-all).
Node.js is always installed
Even without --with-node, Node is present on non-node: bases because the agent CLIs are npm packages. --with-node adds corepack (yarn, pnpm) and lets you pin the version with --node-version.
Agent CLIs
--agent | npm package(s) | Configuration inside the container | agent_type for the profile |
|---|---|---|---|
claude | @anthropic-ai/claude-code, mcp-remote | ~/.claude/settings.json, /workspace/.mcp.json | claude |
copilot | @github/copilot | ~/.copilot/mcp-config.json | copilot-cli |
opencode | opencode-ai | ~/.config/opencode/config.json | generic |
gemini | @google/gemini-cli, mcp-remote | environment only | generic |
agent_type is what heretic uses to decide where to mount the generated MCP config and which home directory to bind — see Agent Profiles.
Changing the image
heretic-cli agents edit claude # guided
heretic-cli agents edit claude --editor # full YAML
# ~/.heretic/agents/claude.yaml
image: my-custom-agent:v2
Per project, via a local override:
# .heretic/cli/claude.yaml
extends: claude
image: my-project-agent:latest
Recipes
Full-stack: JavaScript plus Python
heretic-cli image build \
--agent claude \
--with-python --python-version 3.12 \
-n claude-fullstack
image: claude-fullstack:latest
Agent that builds containers
heretic-cli image build --agent claude --with-docker -n claude-docker
image: claude-docker:latest
dind: true # mounts the host Docker socket
Prefer sidecars over the Docker socket
dind: true gives the container host-level control of Docker. If all you need is a toolchain the image lacks, use build sidecars instead — no socket, no privileged mode.
Polyglot CI image, multi-arch, pushed to a registry
heretic-cli image build --agent claude --with-all \
-r ghcr.io/acme -n heretic-agent -t 1.0.0 -a both -p
Slim image plus builders for the toolchains
heretic-cli image build --agent claude -n claude-slim
heretic-cli image build-sidecar node
heretic-cli image build-sidecar python
image: claude-slim:latest
volumes:
- { source: "${CWD}", target: /workspace }
tool_backends:
sidecars:
- { runtime: node, image: heretic-builder-node:latest }
- { runtime: python, image: heretic-builder-python:latest }
Matching UIDs
Bind-mounted files are created with the container user's uid. If your host account is not uid 1000, build with matching ids so the agent does not leave root-owned or unreadable files behind:
heretic-cli image build --agent-uid "$(id -u)" --agent-gid "$(id -g)"
The image also chowns /opt/sidecar/wrappers to that uid, and the entrypoint refuses to start the tool backends if the directory is not writable (FATAL: /opt/sidecar/wrappers is not writable by …). Changing the container user afterwards with extra.user can trip that check.
Extending an existing image
FROM my-company/dev-base:latest
RUN npm install -g @anthropic-ai/claude-code mcp-remote
# Optional: tool-backend support
COPY sidecar-exec /opt/sidecar/sidecar-exec
COPY ssh-exec /opt/sidecar/ssh-exec
RUN chmod +x /opt/sidecar/* \
&& mkdir -p /opt/sidecar/wrappers \
&& chown 1000:1000 /opt/sidecar/wrappers
ENV PATH="/opt/sidecar/wrappers:${PATH}"
COPY entrypoint.sh /entrypoint.sh
RUN chmod +x /entrypoint.sh
ENTRYPOINT ["/entrypoint.sh"]
CMD ["/bin/bash"]
Generate the pieces so they stay in sync with your CLI version:
heretic-cli image generate --format sidecar-exec > sidecar-exec
heretic-cli image generate --format ssh-exec > ssh-exec
heretic-cli image generate --format entrypoint > entrypoint.sh
Without the baked ENTRYPOINT some features silently do nothing
Root mode (--root) and the tool backends depend on /entrypoint.sh running as the container entrypoint. On an image without it, wrappers are never generated. heretic-cli run --root works around this by bind-mounting the CLI's embedded entrypoint over /entrypoint.sh.
Verifying an image
docker run --rm -it heretic-agent:latest bash -lc 'claude --version; node -v; gh --version'
docker run --rm heretic-agent:latest bash -lc 'ls /opt/sidecar; echo $PATH'
heretic-cli image build --dry-run # inspect the Dockerfile and entrypoint first
Next Steps
- image — every build flag and the generated Dockerfile
- Sidecars — toolchains without image bloat
- Agent Profiles — wiring the image into a profile
- Standalone image guide — the shell-script builder and Windows notes