Skip to Content
Deploy & Operate🧪 Code Execution Sandbox

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:

BackendWhat runs the codeIsolation 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.
daytonaDaytona 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.

  1. Add a gateway token to the .env file 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
  2. 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 .env matters: without it Compose looks for .env in deployment/, the directory of the first file, and doesn’t find the token. Pass the same --env-file and -f list 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.

  3. 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:8888
  • SANDBOX_KERNEL_NAME=docsgpt-python, the kernel that strips secrets from the environment
  • SANDBOX_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 -d

The 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_TYPE set; see Choose an AUTH_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.yaml has the iptables commands.

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-sandbox image, pinned to the same release as arc53/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 runsc RuntimeClass installed, uncomment runtimeClassName: gvisor in 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

SettingDefaultPurpose
SANDBOX_BACKENDjupyterjupyter for the self-hosted runner, daytona for Daytona Cloud.
SANDBOX_GATEWAY_URLhttp://localhost:8888Where the API and worker reach the runner.
SANDBOX_GATEWAY_AUTH_TOKENunsetShared token. Set the same value on the runner and on DocsGPT; the runner won’t start without it.
SANDBOX_KERNEL_NAMEdocsgpt-pythonThe 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_TIMEOUT60Wall-clock limit in seconds for one run.
SANDBOX_MAX_TTL1200Longest time in seconds a kept-alive session may sit idle.
SANDBOX_MAX_SESSIONS32Live sessions per API or worker process. At the limit, the least recently used idle session is closed.
SANDBOX_MEMORY, SANDBOX_CPUS1g, 1.0Resource 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.