Code Execution Sandbox
The Artifact and Code Executor tools run model-written Python in a sandbox, never in the DocsGPT API or worker. Neither tool works until a sandbox is running, which is why neither is enabled by default. Read Document doesn’t need one: it parses files in the Celery worker.
DocsGPT supports two sandbox backends, chosen with SANDBOX_BACKEND:
| Backend | What runs the code | Isolation between sessions |
|---|---|---|
jupyter (default) | docsgpt-sandbox, a Jupyter Kernel Gateway container you run next to DocsGPT. Each session is a kernel process inside it. | Working directory only. |
daytona | Daytona Cloud , one sandbox VM per session. | A separate VM per session. |
One Jupyter runner is one trust domain. Every session runs as the same user in the same container and is separated only by its working directory, so code in one session can read another session’s files. The runner removes secrets from the kernel environment and requires a token on its control API, but it is not a boundary between users who don’t trust each other. For untrusted or multi-tenant use, use Daytona.
Docker Compose
The runner comes as an overlay for the Compose files in a DocsGPT checkout (deployment/docker-compose.yaml or deployment/docker-compose-hub.yaml). Compose builds the runner image from deployment/sandbox the first time. The standalone install (docsgpt up and the one-line installer) has no sandbox option. Use a checkout, Kubernetes, or Daytona instead.
-
Add a gateway token to the
.envfile in the repository root. The runner refuses to start without one, and Compose stops with an error when it is unset:echo "SANDBOX_GATEWAY_AUTH_TOKEN=$(openssl rand -hex 32)" >> .env -
Start the stack with the sandbox overlay, from the repository root:
docker compose --env-file .env \ -f deployment/docker-compose.yaml \ -f deployment/optional/docker-compose.optional.sandbox.yaml \ up -d--env-file .envmatters: without it Compose looks for.envindeployment/, the directory of the first file, and doesn’t find the token. Pass the same--env-fileand-flist to every later command (ps,logs,down). Compose needs the token to read the overlay, and a command without the overlay doesn’t see the runner. -
Enable the tools on an agent, or for every chat through
DEFAULT_CHAT_TOOLS(see Enable the tools).
The overlay sets these on backend and worker, so you don’t add them yourself:
SANDBOX_GATEWAY_URL=http://docsgpt-sandbox:8888SANDBOX_KERNEL_NAME=docsgpt-python, the kernel that strips secrets from the environmentSANDBOX_GATEWAY_AUTH_TOKEN, from your.env
The runner has no published port. It reaches the API and worker over an internal sandbox-net network and reaches the internet over its own sandbox-egress network. Its container is read-only, limited to 256 processes, and capped by SANDBOX_MEMORY (default 1g) and SANDBOX_CPUS (default 1.0).
Block private networks (egress overlay)
By default the runner can reach the internet, which lets code pip install packages or call public APIs. It can also reach your LAN, the Docker host and cloud metadata addresses such as 169.254.169.254. To cut that off, add the egress overlay. It removes the runner’s own route out and sends its traffic through a forward proxy that refuses private destinations:
docker compose --env-file .env \
-f deployment/docker-compose.yaml \
-f deployment/optional/docker-compose.optional.sandbox.yaml \
-f deployment/optional/docker-compose.optional.sandbox-egress.yaml \
up -dThe proxy image in docker-compose.optional.sandbox-egress.yaml (ghcr.io/example/egress-deny-private) is a placeholder. Replace it with a forward proxy of your own, such as Squid or tinyproxy, listening on port 8080 and denying 10.0.0.0/8, 172.16.0.0/12, 192.168.0.0/16, 169.254.0.0/16 and 127.0.0.0/8. Only code that honors HTTP_PROXY and HTTPS_PROXY gets out through it; other raw connections have no route at all.
Neither overlay stops the runner from reaching the API (backend:7091) and the worker, because they share sandbox-net and Compose can’t make that one-way. When you enable the sandbox:
- run DocsGPT with authentication (
AUTH_TYPEset; see Choose anAUTH_TYPE) so the API refuses unauthenticated requests from sandbox code, and - if you can, add host firewall rules that drop traffic from the runner to the API and worker. The header of
docker-compose.optional.sandbox-egress.yamlhas theiptablescommands.
Kubernetes
The runner is deployment/k8s/deployments/sandbox-deploy.yaml, with its NetworkPolicy in deployment/k8s/network-policies/sandbox-egress-policy.yaml. Neither is in kustomization.yaml, so kubectl apply -k doesn’t start them. To enable the sandbox, create the docsgpt-sandbox-gateway Secret, add the three SANDBOX_* variables to the docsgpt-api and docsgpt-worker containers, and apply both files. Kubernetes: Optional code-execution sandbox has the exact steps.
- The manifest runs the published
arc53/docsgpt-sandboximage, pinned to the same release asarc53/docsgpt. Change both tags together when you upgrade. - The NetworkPolicy allows public internet egress and DNS, and blocks private, link-local, carrier-grade NAT, loopback and cloud metadata ranges. On a cluster whose pod network uses those ranges, as most do, that also keeps sandbox code away from the API, worker, Postgres and Redis pods. Only the API and worker pods may connect to the runner, on port 8888. The policy takes effect only with a network plugin that enforces NetworkPolicy, such as Calico or Cilium.
- If your cluster runs NodeLocal DNSCache on an address other than
169.254.20.10, change that address in the policy, or DNS lookups from the runner fail. - The runner Service is
ClusterIP. Don’t expose it outside the cluster. - On nodes with the gVisor
runscRuntimeClass installed, uncommentruntimeClassName: gvisorin the manifest for kernel-level isolation.
pip install or a development machine
Run the published image on its own and point DocsGPT at it:
docker run -d --name docsgpt-sandbox -p 127.0.0.1:8888:8888 \
-e SANDBOX_GATEWAY_AUTH_TOKEN=<same-token-as-in-.env> \
arc53/docsgpt-sandbox:<version>Then set the token in DocsGPT’s .env. SANDBOX_GATEWAY_URL defaults to http://localhost:8888, and SANDBOX_KERNEL_NAME defaults to docsgpt-python, the kernel the image ships:
SANDBOX_GATEWAY_AUTH_TOKEN=<same-token>Use the same <version> as your DocsGPT release. develop tracks the main branch. A container started this way has no egress filtering and can reach the host and your LAN, so keep it to single-user and development setups. To run the gateway from a Python environment without Docker, see the runner notes .
Daytona Cloud
Set SANDBOX_BACKEND=daytona, DAYTONA_API_KEY, and optionally DAYTONA_TARGET for the region. Nothing runs next to DocsGPT. Daytona’s default image lacks the libraries that render presentations, documents, spreadsheets and PDFs, so build a snapshot once and set DAYTONA_SNAPSHOT. See Render libraries on Daytona.
Enable the tools
Turn on Code Executor and Artifact per agent in the agent’s tool picker. To offer them in every chat without an agent, add them to DEFAULT_CHAT_TOOLS in .env:
DEFAULT_CHAT_TOOLS=["memory","read_webpage","scheduler","code_executor","artifact_generator"]To check the setup, ask an agent with Code Executor to run print(1 + 1). An error beginning sandbox unavailable: means the API or worker couldn’t open a session on the runner. Check SANDBOX_GATEWAY_URL, that the tokens match, and the runner’s logs (docker compose ... logs docsgpt-sandbox, or kubectl logs deploy/docsgpt-sandbox).
Settings
| Setting | Default | Purpose |
|---|---|---|
SANDBOX_BACKEND | jupyter | jupyter for the self-hosted runner, daytona for Daytona Cloud. |
SANDBOX_GATEWAY_URL | http://localhost:8888 | Where the API and worker reach the runner. |
SANDBOX_GATEWAY_AUTH_TOKEN | unset | Shared token. Set the same value on the runner and on DocsGPT; the runner won’t start without it. |
SANDBOX_KERNEL_NAME | docsgpt-python | The kernel each session uses. docsgpt-python strips secrets from the kernel environment. The stock python3 kernel passes the gateway’s whole environment to code, so use it only with a bare development gateway that has no other kernel. |
SANDBOX_EXEC_TIMEOUT | 60 | Wall-clock limit in seconds for one run. |
SANDBOX_MAX_TTL | 1200 | Longest time in seconds a kept-alive session may sit idle. |
SANDBOX_MAX_SESSIONS | 32 | Live sessions per API or worker process. At the limit, the least recently used idle session is closed. |
SANDBOX_MEMORY, SANDBOX_CPUS | 1g, 1.0 | Resource caps for the Compose runner container. |
The rest, including the output and file size caps and the Daytona settings, are in the Settings Reference. The runner README covers the isolation model in more depth.