Docker Container Security#
Dify's container security posture relies on a combination of non-root process execution, network segmentation, and kernel-level syscall filtering (for the sandbox service). Explicit Docker runtime hardening directives β cap_drop, read_only, security_opt for seccomp/AppArmor, per-service CPU/memory limits β are largely absent from the generated docker-compose.yaml. Production operators should layer these on top.
The canonical compose file is auto-generated from docker/docker-compose-template.yaml and must not be edited directly .
Non-Root User (API, Web, Agent Runtime)#
All three first-party application containers enforce non-root execution at image-build time β no compose-level user: override is needed.
API container creates a system group/user dify with UID/GID 1001, chowns /app, and drops to that user via USER dify. Pinned versions of security-sensitive packages (expat, libldap, zlib1g, libsqlite3-0) are installed explicitly β see api/Dockerfile.
Web container mirrors the pattern: UID 1001 is configurable via the dify_uid build arg, all /app files are chown-ed, and the final USER dify directive is set . The entrypoint script is copied --chmod=755 to remain executable under the non-root user β see web/Dockerfile.
dify-agent-runtime (langgenius/dify-agent-local-sandbox) also creates a dify user (uid 1001) in its Dockerfile's runtime stage .
Volume permission bootstrap: An init_permissions service runs before api and worker, chown-ing ./volumes/app/storage to 1001:1001 so the non-root user can write to the host-mounted volume .
Network Isolation (Sandbox SSRF Containment)#
The default compose stack implements two distinct SSRF containment architectures:
Workflow Sandbox (sandbox service)#
ssrf_proxy_networkis an internal bridge network (internal: true) with no direct external egress .sandboxjoins onlyssrf_proxy_network, making the Squid proxy atssrf_proxy:3128the sole path for outbound traffic .- The sandbox is pre-configured with
HTTP_PROXY/HTTPS_PROXYpointing to the Squid proxy . - The Squid proxy enforces a deny-by-default policy blocking all RFC-1918 ranges; only
.marketplace.dify.aiis allowed by default .
Agent Sandbox (local_sandbox service)#
The local_sandbox service (Dify Agent V2) has no direct network route to the api service or default network β only two dedicated internal networks:
-
agent_sandbox_network(internal): Shared withagent_backendto allow control-plane access viashellctlon port 5004. Code running inlocal_sandboxcan also reachagent_backendthrough this network β a known limitation acknowledged in the compose file comments. Arbitrary code execution remains possible through the shellctl channel, though the agent runtime respects HTTP(S)_PROXY. -
local_sandbox_proxy_network(internal): Shared only withagent_ssrf_proxy. All egress traffic (exceptlocalhost/127.0.0.1) is forced through the dedicated Squid proxy atagent_ssrf_proxy:3128viaHTTP_PROXY/HTTPS_PROXYenvironment variables.
agent_ssrf_proxy Service#
A dedicated Squid-based forward proxy enforcing path-level ACLs:
- Configuration:
docker/ssrf_proxy/squid-agent.conf.template,docker/ssrf_proxy/squid-common.conf.template,docker/ssrf_proxy/docker-agent-entrypoint.sh - Network membership: Both
default(to reachapiandagent_backendas proxy destinations) andlocal_sandbox_proxy_network(to servelocal_sandbox) - Access control rules:
- ALLOW:
agent_backenddomain with/agent-stub/*path only - ALLOW:
apidomain with/files/*path only (signed upload/download URLs) - ALLOW: Selected private IPs/domains (when
SSRF_PROXY_ALLOW_PRIVATE_IPSorSSRF_PROXY_ALLOW_PRIVATE_DOMAINSare configured) - DENY: All other private network destinations
- ALLOW: External internet traffic
- ALLOW:
Security benefits: Even if arbitrary code executes in the agent sandbox, it cannot directly access internal services beyond the explicitly allowed endpoints. The path-based ACLs ensure only agent-stub endpoints (agent runtime) and files endpoints (signed URLs) are reachable. External API calls (e.g., to LLM providers) pass through the proxy. The private network restriction can be conditionally relaxed via SSRF_PROXY_ALLOW_PRIVATE_IPS and SSRF_PROXY_ALLOW_PRIVATE_DOMAINS environment variables, which allow operators to explicitly permit agent sandbox access to specific internal services when needed.
General Network Topology#
Services that need both inter-service and external access (api, worker, plugin_daemon) join both ssrf_proxy_network and default networks .
Seccomp BPF Filtering (dify-sandbox)#
The sandbox service (langgenius/dify-sandbox) applies a kernel-level seccomp BPF filter to each code-execution child process β separate from and in addition to Docker's container isolation:
- Default action is
ActKillProcess; any syscall not on the whitelist deliversSIGSYS. - The Python/amd64 whitelist lives in
internal/static/python_syscall/syscalls_amd64.goin the dify-sandbox repo . - Network syscalls (
ALLOW_NETWORK_SYSCALLS) are only added whenENABLE_NETWORK: true. - Operators can extend β but not replace β the built-in whitelist via
ALLOWED_SYSCALLSenv var orallowed_syscallsindocker/volumes/sandbox/conf/config.yaml. - The filter uses
SECCOMP_FILTER_FLAG_TSYNCto cover all threads of the child process .
Note: The sandbox healthcheck only hits the parent process
/healthendpoint, which is unaffected by seccomp filter failures β seccomp-related outages are silent at the infrastructure layer .
Filesystem Isolation (dify-agent-runtime / Landlock)#
The local_sandbox service (Agent V2 shell execution) uses Linux Landlock for per-workspace path restrictions β independent of Docker volume mounts:
| Access | Default Paths |
|---|---|
| Read-Write | $HOME, $CWD/.tmp (set as TMPDIR) |
| Read-Write (dev) | /dev/null, /dev/zero, /dev/urandom, /dev/random, /dev/tty |
| Read-Only + Exec | /usr, /bin, /sbin, /lib, /lib64, /etc, /proc, /opt/dify-agent-tools, /snap |
| Denied | Everything else (/tmp, other agents' $HOME, /var, /srv, β¦) |
Requires Linux β₯ 5.13; disable via SHELLCTL_ENABLE_PATH_ISOLATION=false. Custom paths can be added via SHELLCTL_LANDLOCK_RW_PATHS / SHELLCTL_LANDLOCK_RO_PATHS .
Resource Limits (Partial)#
Explicit resource constraints are applied only to select services in the default compose configuration:
- Elasticsearch has
deploy.resources.limits.memory: 2g. - OpenSearch sets
ulimits.memlockto unlimited (soft/hard: -1) andnofileto 65536 β required for memory-mapped I/O, not a security hardening. - OpenGauss runs with
privileged: trueβ a significant trust exception required by that database engine.
CPU limits and per-service memory caps for core services (api, worker, web, sandbox) are absent and should be added by operators for production deployments.
Key Source Files#
| File | Purpose |
|---|---|
api/Dockerfile | Non-root user, pinned security-sensitive packages |
web/Dockerfile | Non-root user (UID 1001) for Next.js container |
docker/docker-compose.yaml | Generated compose; network topology, sandbox config, privileged exception |
docker/docker-compose-template.yaml | Source of truth for compose generation; do not edit docker-compose.yaml directly |
docker/ssrf_proxy/squid.conf.template | Workflow sandbox Squid ACL rules (deny-by-default, RFC-1918 block) |
docker/ssrf_proxy/squid-agent.conf.template | Agent sandbox Squid rules (path-based ACLs for /agent-stub/* and /files/*) |
docker/ssrf_proxy/squid-common.conf.template | Shared ACL definitions for both SSRF proxies |
docker/ssrf_proxy/docker-agent-entrypoint.sh | Agent proxy entrypoint script |
docker/volumes/sandbox/conf/config.yaml | Sandbox runtime config (enable_network, allowed_syscalls) |
| dify-sandbox repo | Seccomp BPF filter implementation and Python/Node syscall whitelists |