Runners
Runner strategies for agent containers: docker (single container), compose (agent + sidecars), and custom (your own compose file)
Heretic CLI supports three runner strategies for starting agent containers. The runner is set in the agent profile with the runner: field.
Comparison
docker | compose | custom | |
|---|---|---|---|
| Use case | Single container | Agent + sidecar services | Full control via your own compose file |
| Manages | One container via Docker API | Multi-service stack via docker compose | Multi-service stack via docker compose |
| Config source | Profile fields | Profile fields + compose: block | .heretic/cli/compose.yaml (your file) |
| Generated file | None | .heretic/temp/<session>/compose.yaml | None (reads existing file) |
Command override (--) | Yes | Yes | Ignored |
Detached mode (-d) | Yes | Yes | Yes |
| Attach | Yes | Yes (to agent service) | Yes (to first service) |
| Requires | Docker daemon | Docker Compose v2 | Docker Compose v2 + your compose file |
runner: docker — Single Container
Default runner. Creates one container using the Docker API directly. All profile fields apply: image, volumes, env, extra, dind, mcp, etc.
image: giglabo/claude-heretic:latest
runner: docker
provider: anthropic
Resource Limits and extra: Fields
The extra: block controls container resource limits, networking, and metadata. All fields are optional.
| Field | Type | Example | Notes |
|---|---|---|---|
memory | string | "4g", "512m" | Units: b, k, m, g (case-insensitive) |
cpus | string | "2.0", "0.5" | Fractional CPUs allowed |
shm_size | string | "2g" | Shared memory (/dev/shm) size |
capabilities | string[] | ["SYS_PTRACE"] | Linux capabilities to add |
privileged | boolean | true | Full host access (use with caution) |
network | string | "host", "bridge", "none" | Network mode |
ports | string[] | ["8080:80", "3000:3000"] | Format: host:container |
user | string | "1000:1000" | uid:gid or username |
hostname | string | "my-agent" | Container hostname |
labels | object | {team: platform} | Merged with heretic default labels |
Port format: "host:container" — for example, "8080:80" maps host port 8080 to container port 80.
extra:
memory: "8g"
cpus: "4.0"
shm_size: "2g"
network: host
ports:
- "3000:3000"
- "5173:5173"
capabilities:
- SYS_PTRACE
privileged: false
runner: compose — Agent + Sidecars
Use when the agent needs companion services (database, Redis, local MCP server, etc.). The CLI generates a temporary docker-compose.yaml at .heretic/temp/<session>/compose.yaml and passes it to docker compose up.
The agent container is always the service named agent. Additional services are defined in the compose: profile block.
image: giglabo/claude-heretic:latest
runner: compose
provider: anthropic
extra:
ports:
- "3000:3000"
compose:
services:
db:
image: postgres:16
environment:
POSTGRES_PASSWORD: devpass
ports:
- "5432:5432"
redis:
image: redis:7-alpine
ports:
- "6379:6379"
Generated Compose YAML Structure
When using runner: compose, the CLI generates a complete compose file that merges your profile fields into the agent service:
version: "3.8"
services:
agent: # always "agent" — do not use this name in compose.services
image: ...
environment: ... # all profile env vars + secrets
volumes: ... # all profile volumes + MCP + session mounts
labels:
heretic.managed: "true"
heretic.agent: <name>
heretic.project: <dir>
heretic.session: <session>
stdin_open: true
tty: true
# extra.* fields mapped directly:
network_mode: ...
ports: [...]
cap_add: [...]
privileged: ...
user: ...
hostname: ...
deploy:
resources:
limits:
memory: ...
cpus: ...
shm_size: ...
command: [...] # if command or -- override present
db: ... # from compose.services
redis: ... # from compose.services
compose: Profile Block
| Field | Type | Description |
|---|---|---|
compose.services | object | Additional services merged alongside agent. Any docker-compose v3.8 service config is valid. |
compose.networks | object | Top-level networks: block for custom network definitions. |
Named Volumes
compose.volumes (named volumes) is defined in the profile type but is not yet written to the generated compose file. Define named volumes directly in compose.services using volumes: at the service level, or use bind mounts via the profile volumes: array.
Networking Between Services
In compose mode, all services share a default network and can reach each other by service name:
compose:
services:
db:
image: postgres:16
environment:
POSTGRES_PASSWORD: devpass
redis:
image: redis:7-alpine
The agent container can reach PostgreSQL at db:5432 and Redis at redis:6379 — no extra configuration needed.
To use a custom network:
compose:
networks:
internal:
driver: bridge
services:
db:
image: postgres:16
networks:
- internal
Sidecar MCP Server Example
Run a local MCP server as a sidecar and connect the agent to it:
image: giglabo/claude-heretic:latest
runner: compose
provider: anthropic
mcp:
- name: my-local-server
command: npx
args: ["-y", "mcp-remote", "http://mcp-server:8080/sse"]
compose:
services:
mcp-server:
image: my-mcp-server:latest
environment:
API_KEY: "${MY_API_KEY}"
The agent's MCP config references mcp-server by service name. The compose network makes http://mcp-server:8080 reachable from the agent container.
Build Sidecars & SSH
For routing build commands (npm, python3, go, etc.) to sidecar containers or a remote host, see Sidecars and SSH Host Access.
runner: custom — Your Own Compose File
Use when you need full control over the compose spec — advanced networking, multiple named volumes, build contexts, health checks, depends_on conditions, etc.
You write .heretic/cli/compose.yaml yourself. The CLI runs it with docker compose up and injects resolved env vars (secrets + env: block + GitHub tokens) into the process environment so your compose file can reference them via ${VAR} syntax.
Setup
# Generate a starter template
heretic-cli local-init --compose
# Edit to suit your project
vim .heretic/cli/compose.yaml
# Set runner: custom in the profile
Profile
image: giglabo/claude-heretic:latest
runner: custom
provider: anthropic
secrets:
ANTHROPIC_API_KEY: ~/.heretic/get-anthropic-key.sh
env:
ANTHROPIC_API_KEY: "${ANTHROPIC_API_KEY}"
MY_DB_PASSWORD: "${MY_DB_PASSWORD}"
Generated Template
Running heretic-cli local-init --compose creates a starter template at .heretic/cli/compose.yaml:
version: "3.8"
services:
agent:
image: giglabo/claude-heretic:latest
volumes:
- .:/workspace
environment:
- ANTHROPIC_API_KEY # value comes from host env (injected by CLI)
stdin_open: true
tty: true
working_dir: /workspace
network_mode: host
# Add additional services as needed
# db:
# image: postgres:16
# environment:
# POSTGRES_PASSWORD: devpass
# ports:
# - "5432:5432"
What the CLI Injects
When running docker compose against your custom file, the CLI injects all of these as host environment variables (available as ${VAR} in your compose YAML):
- Resolved secrets (from profile
secrets:) - Profile
env:values GH_TOKEN,GITHUB_TOKEN(from global settings)GH_COPILOT_TOKEN,GITHUB_COPILOT_TOKEN(forprovider: copilotonly)
Important Differences from runner: compose
| Feature | compose | custom |
|---|---|---|
| MCP mounts | Auto-generated and mounted | You manage all mounts |
| Session directories | Auto-created (~/.claude, etc.) | You manage all mounts |
| Onboarding seeds | Auto-created (.claude.json) | Not managed |
extra: fields | Applied to agent service | Ignored |
compose: block | Merged into generated YAML | Ignored |
Command override (--) | Applied | Ignored |
| Attach target | agent service | First service alphabetically |
Custom Runner Limitations
With runner: custom, the CLI does NOT set up MCP mounts, session directories, ~/.claude mounts, or onboarding seeds. You manage all container configuration in your compose file.
Full Custom Compose Example
A production-like setup with health checks, depends_on conditions, named volumes, and custom networking:
version: "3.8"
services:
agent:
image: giglabo/claude-heretic:latest
environment:
- ANTHROPIC_API_KEY
- GH_TOKEN
- NODE_ENV=development
volumes:
- ${PWD}:/workspace
- agent-home:/home/agent
working_dir: /workspace
stdin_open: true
tty: true
networks:
- app
depends_on:
db:
condition: service_healthy
redis:
condition: service_started
db:
image: postgres:16
environment:
POSTGRES_PASSWORD: devpass
POSTGRES_DB: myapp
volumes:
- pgdata:/var/lib/postgresql/data
ports:
- "5432:5432"
healthcheck:
test: ["CMD-SHELL", "pg_isready -U postgres"]
interval: 5s
timeout: 5s
retries: 5
networks:
- app
redis:
image: redis:7-alpine
ports:
- "6379:6379"
networks:
- app
networks:
app:
driver: bridge
volumes:
pgdata:
agent-home:
This setup:
- Waits for PostgreSQL to be healthy before starting the agent
- Persists database data in the
pgdatanamed volume across restarts - Persists the agent home directory in
agent-home(keeps.claudeconfig, bash history, etc.) - Uses a custom
appnetwork for service-to-service communication - Exposes PostgreSQL on the host for direct debugging (
localhost:5432)
When to Use Each Runner
| Scenario | Runner |
|---|---|
| Solo agent, no services needed | docker |
| Agent needs a database or cache for the current project | compose |
| Agent needs a local MCP server as a sidecar | compose |
You need named volumes, health checks, build contexts, or depends_on | custom |
| You have an existing docker-compose setup to integrate with | custom |
| You need advanced networking (custom bridges, container-to-container) | custom |
Next Steps
- Sidecars — Route build commands to language-specific sidecar containers
- SSH Host Access — Route commands to a remote host via SSH
- Agent Profiles — Full profile YAML reference
- MCP Servers — Configure MCP tools for your agents
- Docker Images — Choose or build agent images
- Sessions — Run multiple isolated instances