This guide explains how the Docker images and Compose stack are wired for Assistant.
The agent build is multi-stage:
ui-buildstage (node:22-bookworm)- Installs frontend deps from
agent/frontend/package*.json - Builds the workspace/admin frontend bundle with Vite
- Installs frontend deps from
- Runtime stage (
python:3.12-slim)- Installs runtime OS packages (
pandoc,weasyprint) - Installs Python dependencies from
requirements.txt - Copies Python source code and compiled UI assets
- Installs runtime OS packages (
This keeps UI build tooling out of the final runtime image while shipping compiled assets inside the Python container.
The sandbox image is separate and includes tooling useful for command execution (curl, git, jq, unzip, etc.) plus server.py exposed via Uvicorn.
In production defaults, this image is used both for the sandbox broker and per-command run containers (SANDBOX_IMAGE, default assistant-sandbox:latest).
Main services in docker-compose.yml:
postgresclamavagent-apidownloadertask-agentworkspace-indexerreminder-schedulerproject-schedulerdeep-research-agentsupervisorheartbeatollamaollama-model-puller(one-shot startup helper)sandbox
Most Python roles share one image (build: ./agent) and are selected by AGENT_ROLE.
app_net: normal application/data plane traffic.sandbox_net(internal: true): sandbox control-plane traffic.
sandbox is attached only to sandbox_net. Services that call sandbox endpoints (agent-api, task-agent) are attached to both networks.
Per-command run containers are created by the sandbox broker on ephemeral one-container bridge networks (not on app_net).
Default bind mounts:
./data/postgres→ PostgreSQL data./data/share→ shared workspace./data/private/artifacts→ private inbound artifacts./data/ollama→ Ollama model cache./data/clamav→ ClamAV signatures/state./config(read-only) →/app/config
The common config mount is declared once via the x-agent-config-volume anchor and reused by all agent-role services.
docker-compose.yml uses anchors to avoid duplication:
x-agent-secrets: shared environment variables sourced from.envx-agent-config-volume: read-only config bind mountx-agent-common: shared build/restart/environment/network defaults
Role services (task-agent, downloader, supervisor, etc.) extend x-agent-common and override only role-specific fields.
task-agent supports horizontal scaling with:
deploy:
replicas: ${TASK_AGENT_WORKERS:-1}Workers safely claim jobs via DB locking (FOR UPDATE SKIP LOCKED).
The default hardened sandbox settings in Compose include:
- broker container is read-only with
tmpfs: /tmp security_opt: no-new-privileges:true- broker drops all Linux capabilities
- run containers drop
NET_RAW - CPU/memory/PID limits for run containers
- Docker socket mounted only into broker (never into run containers)
Important environment variables:
SANDBOX_SHARED_ROOT=/data/shareSANDBOX_SHARED_ROOT_HOST=${PWD}/data/share(override when needed)SANDBOX_RUN_NETWORK_MODE=per_run_bridgeSANDBOX_RUN_CPUS,SANDBOX_RUN_MEMORY_BYTES,SANDBOX_RUN_PIDS_LIMIT
- Keep
./configmounted read-only so runtime prompt/config files stay operator-controlled. - If you change host paths, update all relevant bind mounts consistently.
- For gVisor (
runsc), use a Compose override and validate networking carefully before production use.