Sandbox Code Execution#
Dify runs two distinct sandboxed execution environments in separate containers:
| Sandbox | Image | Port | Use case |
|---|---|---|---|
| dify-sandbox (legacy) | langgenius/dify-sandbox:0.2.15 | 8194 | Code nodes in Workflows (Python3, JS, Jinja2) |
| dify-agent-runtime | langgenius/dify-agent-local-sandbox:1.17.1 | 5004 | Agent V2 shell jobs |
Both are declared in docker/docker-compose-template.yaml as the sandbox and local_sandbox services respectively.
Legacy dify-sandbox (Code Nodes)#
The legacy sandbox is an external Go service (langgenius/dify-sandbox) running image langgenius/dify-sandbox:0.2.15 . It accepts HTTP requests and secures each execution with two independent layers:
- Docker isolation β container-level network and filesystem separation
- seccomp BPF β a per-child-process kernel syscall filter defaulting to
ActKillProcessfor any unlisted syscall
Languages supported: Python3, Node.js, and Jinja2 (which runs as Python3 at runtime) .
Configuration is in docker/volumes/sandbox/conf/config.yaml: port 8194, max_workers: 4, enable_network: true, and an optional allowed_syscalls list for extending the seccomp whitelist . The WORKER_TIMEOUT env var (default 15s) and PIP_MIRROR_URL for a custom PyPI mirror are set in the compose file .
Dependency caching: the compose file mounts ./volumes/sandbox/dependencies:/dependencies , allowing pre-downloaded Python packages to survive container restarts.
API Layer Integration (CodeExecutor)#
The CodeExecutor class in api/core/helper/code_executor/code_executor.py is the single entry point for all code execution from the API layer.
- Endpoint:
CODE_EXECUTION_ENDPOINT(defaulthttp://sandbox:8194) βPOST /v1/sandbox/run - Auth:
X-Api-Keyheader usingCODE_EXECUTION_API_KEY - Timeouts: connect 10 s, read 60 s, write 10 s (configurable)
- Connection pooling:
httpx.Limitswith configurablemax_connections(default 100) andmax_keepalive_connections(default 20)
Call flow: execute_workflow_code_template() β language transformer's transform_caller() (wraps user code, serializes inputs as base64) β execute_code() β POST /v1/sandbox/run β response parsed by transform_response().
Language transformers:
| Transformer | Language sent to sandbox | Preload script |
|---|---|---|
Python3TemplateTransformer | python3 | None |
NodeJsTemplateTransformer | nodejs | None |
Jinja2TemplateTransformer | python3 | Yes β initializes Jinja2 env before user template runs |
dify-agent-runtime (Agent V2 Shell)#
The dify-agent-runtime (langgenius/dify-agent-local-sandbox:1.17.1) is a Go rewrite of the original Python shellctl package, built for Agent V2 shell job execution . This image supports multiple execution backends; the container, network, and filesystem details below apply to the local backend only. See the Backend Configuration section for how backends differ.
Build: a two-stage Dockerfile β a golang:1.26 builder compiles five Go binaries (shellctl, shellctl-runner, shellctl-sanitize-pty, shellctl-runner-exit, dify-agent) with CGO_ENABLED=0, then copies them into a python:3.12-slim-bookworm runtime image . The runtime image includes iproute2 to satisfy OpenShell backend requirements.
Pre-installed toolchain in the runtime image :
| Tool | Version |
|---|---|
| Python | 3.12 (base image) |
| Node.js | 24.20.0 |
| pnpm | 11.9.0 |
| uv | 0.8.9 |
| System tools | git, jq, tmux, ripgrep, openssh-client, procps, unzip, zip, less, curl, iproute2 |
Runtime dependency installation: Agent job configurations (DifyShellLayerConfig) can declare cli_tools with install_commands lists; these bootstrap scripts are executed by shellctl-runner when a workspace is first created . pip, uv, npm, and pnpm are all available in the container for these install steps.
Filesystem isolation (Landlock) β local backend: each job runs with per-workspace path restrictions β read-write only to $HOME and the workspace directory (cwd); read-only to /usr, /bin, /lib, etc.; everything else denied . The workspace directory itself serves as both the command working directory and temporary storage space, with TMPDIR, TMP, and TEMP environment variables pointing directly to it. Requires Linux β₯ 5.13; disable via SHELLCTL_ENABLE_PATH_ISOLATION=false.
Environment variables β local backend:
SHELLCTL_AUTH_TOKENβ authentication token for the shellctl endpoint ; the local backend connects viaDIFY_AGENT_SHELLCTL_ENTRYPOINTHTTP_PROXY=http://agent_ssrf_proxy:3128β routes HTTP traffic through the agent SSRF proxyHTTPS_PROXY=http://agent_ssrf_proxy:3128β routes HTTPS traffic through the agent SSRF proxyNO_PROXY=localhost,127.0.0.1β bypasses proxy for localhost traffic
Network isolation β local backend: local_sandbox is not connected to the default network. It is connected to two internal networks:
agent_sandbox_networkβ shared withagent_backend, allowsagent_backendto communicate with the shellctl endpoint on port 5004local_sandbox_proxy_networkβ shared only withagent_ssrf_proxy, provides proxy access for outbound traffic
This network isolation prevents direct access to internal services like api.
Traffic routing β local backend: All HTTP/HTTPS traffic (except localhost and 127.0.0.1) is automatically routed through agent_ssrf_proxy on port 3128. The proxy enforces strict access control:
- β
Allows
agent_backend/agent-stub/*endpoints - β
Allows
api/files/*endpoints (for signed upload/download URLs) - β Allows external internet access (e.g., to LLM APIs)
- β
Allows additional private IPs or domains configured via
SSRF_PROXY_ALLOW_PRIVATE_IPSandSSRF_PROXY_ALLOW_PRIVATE_DOMAINSenvironment variables - β Denies all other internal service access (default behavior when allowlist variables are not set)
Shellctl communication (agent_backend β local_sandbox:5004) uses the agent_sandbox_network directly.
Security considerations β local backend: The proxy and network isolation prevent SSRF attacks from malicious code running in the sandbox. Code executed in the sandbox cannot directly reach internal services. Access to the Dify API is restricted to the /files/* endpoint for signed file URLs only. Access to agent_backend is restricted to /agent-stub/* endpoints only. Known limitation: code in the sandbox can still reach agent_backend via the agent_sandbox_network, though the primary goal is preventing access to api and other services.
Backend Configuration#
Agent V2 sandboxes support multiple runtime backends. Select a backend via DIFY_AGENT_RUNTIME_BACKEND:
| Backend | Description | Configuration reference |
|---|---|---|
| local (default) | Uses the local_sandbox container with SSRF-proxy network topology (described above) | Environment variables in docker/docker-compose-template.yaml |
| enterprise | Proprietary enterprise backend | Contact Dify for details |
| e2b | E2B cloud sandboxes | DIFY_AGENT_E2B_API_KEY, DIFY_AGENT_E2B_TEMPLATE |
| openshell | One sandbox per Execution Binding on a self-hosted NVIDIA OpenShell gateway (β₯ 0.0.106) | Full variable reference in docker/envs/core-services/dify-agent.env.example; operator guide at dify-agent/docs/dify-agent/guide/openshell.md |
OpenShell Backend#
The OpenShell backend runs each Execution Binding in its own isolated sandbox on a self-hosted NVIDIA OpenShell gateway (version 0.0.106 or newer). Unlike the local backend, which manages one shared container through Docker Compose, OpenShell creates and destroys sandboxes dynamically through the gateway gRPC API.
Execution model:
- Sandboxes are created and managed through the OpenShell gateway gRPC API
- Each Execution Binding gets its own isolated sandbox
- shellctl is bootstrapped through idempotent gateway exec on acquire
- Communication happens through authenticated ForwardTcp tunnels (exec scripts travel via stdin so tokens never enter gateway-logged argv)
- Session tokens auto-renew ahead of expiry
Isolation and security:
- Landlock filesystem policy is managed at the sandbox level by the gateway; shellctl's path isolation is disabled (
SHELLCTL_ENABLE_PATH_ISOLATION=false) - Network egress is opt-in via
DIFY_AGENT_OPENSHELL_EGRESS_ALLOW: a comma-separatedhost:portallowlist (no scheme or path). When set, sandbox egress is restricted to exactly those endpoints. When empty, the policy carries no network rules and egress follows the gateway/driver default - Sandboxes reconnect to the gateway through
host.openshell.internal - The SSRF-proxy network topology described above for the local backend does not apply to OpenShell sandboxes
Home Snapshots:
- Stored as directory copies on an operator-provided shared volume mounted into every sandbox
- Restore fails rather than falling back when a snapshot is missing
- Production multi-tenant deployments must use one OpenShell workspace and one dedicated volume per tenant; snapshot paths carry deterministic digests so product identifiers stay out of gateway metadata
- Snapshot deletion runs a short-lived maintenance sandbox granted only that tenant's snapshot root
Lifecycle:
- New Bindings are initialized and then stopped
- Acquire starts a stopped sandbox when needed, verifies its Home and Workspace, ensures shellctl is running, and opens a fresh lease-local tunnel
- Release closes only that lease's HTTP client and tunnel; it does not stop the sandbox or shellctl
- Subsequent operations can reuse the running sandbox, and releasing one lease does not interrupt another lease on the same Binding
- There is no automatic idle-stop or sandbox TTL; operators must account for running resources
- Destroy deletes the sandbox and its workspace; treated as idempotent on NOT_FOUND
Setup requirements:
- An OpenShell gateway β₯ 0.0.106, reachable from
agent_backendatDIFY_AGENT_OPENSHELL_GATEWAY_ENDPOINT - Gateway credentials via
DIFY_AGENT_OPENSHELL_BEARER_TOKENor mTLS bundle paths - A self-built runtime image from the current checkout, including shellctl and
iproute2; setDIFY_AGENT_OPENSHELL_SANDBOX_IMAGEto the exact built image reference DIFY_AGENT_OPENSHELL_DRIVER_CONFIGas JSON mounting the shared Home Snapshot volume atDIFY_AGENT_OPENSHELL_SHARED_MOUNT_PATHDIFY_AGENT_OPENSHELL_SHELLCTL_AUTH_TOKENrequired for shellctl authentication- Sandboxes must reach
DIFY_AGENT_STUB_API_BASE_URLandDIFY_AGENT_SANDBOX_FILES_BASE_URL
See the OpenShell operator guide for deployment instructions, volume initialization, and validation.
Key Source Files#
| File | Purpose |
|---|---|
api/core/helper/code_executor/code_executor.py | CodeExecutor β HTTP client, language dispatch, error handling |
dify-agent-runtime/docker/Dockerfile | dify-agent-runtime image build: Go binaries + Node.js/Python/uv toolchain |
docker/volumes/sandbox/conf/config.yaml | Legacy sandbox runtime config (port, workers, network, syscalls) |
docker/docker-compose-template.yaml | Both sandbox service definitions, volume mounts, env vars |
docker/ssrf_proxy/squid-agent.conf.template | Agent SSRF proxy Squid configuration (access control rules) |
| langgenius/dify-sandbox | External repo for the legacy sandbox (seccomp rules, Python/Node runtime) |