Plugin Daemon Architecture#
The Dify API communicates with the plugin daemon — a separate Go-based service (default port 5002) — for all plugin execution. The API never runs plugin code directly; every plugin invocation, model dispatch, tool call, and credential validation is proxied over HTTP to the daemon .
Dify API ──(HTTP/SSE)──► Plugin Daemon :5002
X-Api-Key, X-Plugin-ID headers
URL path: plugin/{tenant_id}/...
The daemon maintains its own PostgreSQL database (dify_plugin, separate from the main dify DB) and its own filesystem layout for installed plugin packages .
Request Routing: Headers and URL Structure#
All requests flow through BasePluginClient in api/core/plugin/impl/base.py. The request routing uses two complementary identification mechanisms:
- Tenant ID — in the URL path: Every endpoint is prefixed with
plugin/{tenant_id}/, scoping the request to a specific workspace. For example:plugin/{tenant_id}/dispatch/llm/invoke. - Plugin ID — in the
X-Plugin-IDheader: The specific installed plugin to invoke is set in the HTTP header on every dispatch call, e.g."X-Plugin-ID": plugin_id. This is consistent across all plugin client implementations: model, tool, agent, trigger, datasource, and oauth .
A third header, X-Api-Key, carries PLUGIN_DAEMON_KEY and is injected by _prepare_request() on every call for daemon authentication . An optional traceparent header is appended when OpenTelemetry is enabled .
URL Patterns#
| Operation | URL Pattern |
|---|---|
| List model providers | GET plugin/{tenant_id}/management/models |
| Invoke LLM | POST plugin/{tenant_id}/dispatch/llm/invoke |
| Validate credentials | POST plugin/{tenant_id}/dispatch/model/validate_provider_credentials |
| Invoke tool | POST plugin/{tenant_id}/dispatch/tool/invoke |
Response Format#
Responses are wrapped in a PluginDaemonBasicResponse envelope; non-zero code values trigger structured error handling in _handle_plugin_daemon_error(). Streaming endpoints (LLM, embeddings, rerank) use SSE; the client reads newline-delimited JSON lines via _stream_request() .
Configuration#
Configured in PluginConfig (api/configs/feature/__init__.py):
| Variable | Default | Purpose |
|---|---|---|
PLUGIN_DAEMON_URL | http://localhost:5002 | Base URL for the plugin daemon |
PLUGIN_DAEMON_KEY | plugin-api-key | Shared secret injected as X-Api-Key |
PLUGIN_DAEMON_TIMEOUT | 600.0 s | Request timeout (set None to disable) |
In Docker Compose the URL is typically http://plugin_daemon:5002 . The HTTP connection pool is bounded at 50 keep-alive / 100 max connections .
Version Synchronization#
The plugin daemon is versioned in lockstep with the Dify API. Protocol changes between releases cause complete communication failures if the versions diverge . Known pairings:
| Dify API | Plugin Daemon |
|---|---|
| 1.13.x | 0.5.x |
| 1.14.x | 0.6.x |
| 1.15.0 | 0.6.3-local |
When the daemon version does not match the API, the daemon logs "failed to find the version of the plugin sdk" and the API returns connection errors .
Upgrade Checklist#
After any major version upgrade:
- Run the Alembic migration:
docker compose exec api flask upgrade-db - Run the plugin backfill:
docker compose exec api flask backfill-plugin-auto-upgrade— normalizes plugin records after version identifier changes - Confirm the daemon image tag matches the Dify release
- Remove stale plugin version directories from
PLUGIN_INSTALLED_PATH
Common Upgrade Failures#
- "record not found" / "Plugin table missing" (1.15.0+): Stale plugin version directories remain on disk; the backfill command resolves this .
- "Credential with id … not found" (1.14.x):
model_typeenum values were renamed without a data migration; see Plugin Database Integrity for the SQL fix . - Duplicate
plugin_unique_identifier(SQLSTATE 23505): Daemon crashes on startup during index creation; deduplicate thedify_plugin.pluginstable before restarting .
Key Source References#
| Resource | Link |
|---|---|
BasePluginClient (HTTP layer) | api/core/plugin/impl/base.py |
PluginModelClient (model dispatch) | api/core/plugin/impl/model.py |
PluginConfig (configuration) | api/configs/feature/init.py |
| Plugin Database Integrity patterns | Knowledge Base article |