Docker Networking#
Dify's Docker Compose stack uses Docker's embedded DNS resolver for inter-service communication: containers refer to each other by their service name (e.g., api, plugin_daemon, redis), and Docker resolves those names to container IPs on a shared bridge network. All authoritative definitions live in docker/docker-compose-template.yaml (the generated docker-compose.yaml must not be edited directly).
Network Topology#
The stack defines six named bridge networks :
| Network | Driver | internal | Purpose |
|---|---|---|---|
default | bridge | no | General inter-service traffic; has external access |
ssrf_proxy_network | bridge | yes | Restricted network for sandbox/proxy; no direct external egress |
agent_sandbox_network | bridge | yes | Shared only by agent_backend and local_sandbox; allows agent_backend to reach local_sandbox on port 5004 (shellctl control channel) |
local_sandbox_proxy_network | bridge | yes | Shared only by local_sandbox and agent_ssrf_proxy; allows local_sandbox to route all egress traffic through the proxy without gaining direct access to the api service |
milvus | bridge | no | Isolated Milvus cluster (etcd, MinIO, milvus-standalone) |
opensearch-net | bridge | yes | Isolated OpenSearch cluster |
Service β network membership :
| Service(s) | Networks |
|---|---|
api, api_websocket, worker, worker_beat | ssrf_proxy_network + default |
plugin_daemon | ssrf_proxy_network + default |
ssrf_proxy | ssrf_proxy_network + default |
sandbox | ssrf_proxy_network only |
agent_backend | default + agent_sandbox_network |
agent_ssrf_proxy | default + local_sandbox_proxy_network |
local_sandbox | agent_sandbox_network + local_sandbox_proxy_network only (no default network) |
web, nginx, redis, db_postgres, db_mysql | default only |
Two services are intentionally isolated from the default network for security:
sandboxβssrf_proxy_networkonly; cannot reach the external network directly, all outbound traffic routes through the Squid SSRF proxy .local_sandboxβagent_sandbox_network+local_sandbox_proxy_networkonly; isolated fromapiand other internal services. All egress traffic (except toagent_backendand localhost) is forced throughagent_ssrf_proxy, which enforces path-based ACLs: only/agent-stub/*onagent_backendand/files/*onapiare allowed. This prevents SSRF attacks from malicious code running in the agent sandbox.
ββ default (external access) ββββββββββββββββββββββββββββββββββββββββββββββββ
β nginx β api / worker / api_websocket / plugin_daemon / web β
β redis db_postgres agent_backend agent_ssrf_proxy β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β ssrf_proxy_network (internal: true)
βββ api, worker, worker_beat, api_websocket
βββ plugin_daemon
βββ ssrf_proxy (Squid :3128)
βββ sandbox β only here (no external egress)
agent_sandbox_network (internal: true)
βββ agent_backend (also on default)
βββ local_sandbox β shellctl control channel (:5004)
local_sandbox_proxy_network (internal: true)
βββ agent_ssrf_proxy (also on default)
βββ local_sandbox β proxy egress path (:3128, ACL-restricted)
Key Inter-Service DNS Names and Ports#
All default service URLs use Docker DNS names, configured in docker/.env and the compose template :
| Connection | DNS address | Default port | Variable |
|---|---|---|---|
| API β Plugin Daemon | http://plugin_daemon | 5002 | PLUGIN_DAEMON_URL |
| Plugin Daemon β API (inner) | http://api | 5001 | PLUGIN_DIFY_INNER_API_URL / DIFY_INNER_API_URL |
| API β Agent Backend | http://agent_backend | 5050 | AGENT_BACKEND_BASE_URL |
| Agent Backend β Plugin Daemon | http://plugin_daemon | 5002 | DIFY_AGENT_PLUGIN_DAEMON_URL |
| Agent Backend β API (inner) | http://api | 5001 | DIFY_AGENT_INNER_API_URL |
| Agent Backend β Local Sandbox | http://local_sandbox | 5004 | DIFY_AGENT_SHELLCTL_ENTRYPOINT (via agent_sandbox_network) |
| Local Sandbox β Agent SSRF Proxy | http://agent_ssrf_proxy | 3128 | HTTP_PROXY / HTTPS_PROXY (set in environment) |
| Web (SSR) β API | http://api | 5001 | SERVER_CONSOLE_API_URL |
| Nginx β API | api:5001 | 5001 | Nginx upstream |
| Nginx β WebSocket | api_websocket:5001 | 5001 | NGINX_SOCKET_IO_UPSTREAM |
| API/Worker β Sandbox | http://sandbox | 8194 | CODE_EXECUTION_ENDPOINT |
| API/Worker β Redis | redis | 6379 | REDIS_HOST |
| API/Worker β DB | db_postgres or db_mysql | 5432 / 3306 | DB_HOST |
Critical: Never use
localhostfor cross-container URLs in Docker Compose.localhostresolves to the container itself, not a peer service. The most common misconfiguration is leavingPLUGIN_DAEMON_URL=http://localhost:5002in a Dockerized deployment .
Plugin Daemon Connectivity#
The plugin daemon requires two bidirectional paths :
- API β Daemon (
http://plugin_daemon:5002): Used for all plugin invocations. RequiresPLUGIN_DAEMON_URLto use the service name, notlocalhost. Authenticated viaPLUGIN_DAEMON_KEY(sent asX-Api-Key). - Daemon β API (
http://api:5001): The daemon calls back to the Dify API for inner-API operations. Configured viaDIFY_INNER_API_URLon the daemon, authenticated withPLUGIN_DIFY_INNER_API_KEY.
The debugging/remote-install port (5003) is the only port published to the host in default configuration . The main API port (5002) is internal-only β accessible over the default network by service name, but not exposed to the Docker host. See Plugin Daemon Port Configuration for the full variable mapping.
In source (dev) mode, the daemon runs in Docker while the API runs on the host, requiring PLUGIN_DIFY_INNER_API_URL=http://host.docker.internal:5001 on macOS/Windows, or the host bridge IP on Linux .
web Service SSR Routing#
The Next.js web container's server-side rendering process must reach the API over the internal network. SERVER_CONSOLE_API_URL defaults to http://api:5001 in the compose template . If you override CONSOLE_API_URL with a public hostname (e.g., for browser access), also explicitly set SERVER_CONSOLE_API_URL to the internal DNS name. Leaving it pointing to an external hostname that doesn't resolve inside the container is the most common cause of silent SSR failures .
Key Source Files#
| File | Purpose |
|---|---|
docker/docker-compose-template.yaml | Authoritative network/service definitions |
docker/.env.example | All configurable variables with defaults |
api/configs/feature/__init__.py | Application-layer defaults for PLUGIN_DAEMON_URL, PLUGIN_REMOTE_INSTALL_*, etc. |
| Plugin Daemon Port Configuration | Full breakdown of the 6-variable port-mapping system |
| Plugin Daemon Communication | HTTP client details (pooling, auth headers, streaming) |
| Sandbox Network Isolation | ssrf_proxy_network deny-by-default policy and exceptions |