Documentsdify
Plugin Lifecycle Management
Plugin Lifecycle Management
Type
Topic
Status
Published
Created
Jul 13, 2026
Updated
Jul 13, 2026

Plugin Lifecycle Management#

Plugin lifecycle management in Dify covers three interconnected concerns: per-category auto-upgrade strategies that control how each tenant's plugins are updated, data migration utilities that convert legacy provider state to the plugin model, and CLI commands that operators run during upgrades and migrations.

The system lives primarily in:


Auto-Upgrade Strategy Model#

Each tenant stores one upgrade strategy per plugin category in the tenant_plugin_auto_upgrade_strategies table. The category enum (TenantPluginAutoUpgradeCategory) has six values: tool, model, extension, agent-strategy, datasource, and trigger.

A strategy row (TenantPluginAutoUpgradeStrategy) carries:

FieldTypeNotes
categoryenumUnique per (tenant_id, category)
strategy_settingenumdisabled / fix_only / latest
upgrade_modeenumall / partial / exclude
include_plugins / exclude_pluginsJSONPlugin ID lists for partial/exclude modes
upgrade_time_of_dayintIndexed; slot distributed via tenant SHA-256 hash

Default strategies differ by category: model defaults to latest; all other categories default to fix_only . Upgrade check times are spread across 15-minute aligned slots using a SHA-256 hash of the tenant ID to prevent scheduler thundering-herd .

Schema evolution: The table was initially created with a single workspace-level row per tenant (migration 2025_07_23_1508), then extended with a category column and a new unique constraint on (tenant_id, category) (migration 2026_06_15_1200). A MySQL/OceanBase compatibility migration (2025_11_15_2102) converted exclude_plugins/include_plugins from PostgreSQL ARRAY to JSON.


CLI Commands#

All commands are Flask Click commands invoked as flask <command>.

Plugin Auto-Upgrade#

backfill-plugin-auto-upgrade — the primary post-upgrade backfill command. Run this after the schema migration to populate missing category rows for all tenants.

flask backfill-plugin-auto-upgrade [--tenant-id <id>] [--limit N] [--batch-size 500] [--dry-run]

Internally it calls PluginAutoUpgradeService.backfill_strategy_categories() for each candidate tenant (those with fewer strategy rows than the six categories). The backfill:

  1. Reads the legacy single-row strategy as the source.
  2. For pure defaults: creates new rows with model=latest and others=fix_only.
  3. For customized strategies with plugin lists: queries the plugin daemon to assign each plugin ID to its category, then narrows the include/exclude list per category row.

Candidate tenants are identified via a SQL query that groups by tenant_id and filters where the distinct category count is less than six .

Data Migration#

migrate-data-for-plugin — calls PluginDataMigration.migrate(), which rewrites provider name strings across 10+ tables (e.g., providers, provider_models, tenant_default_models) from the legacy short format (google) to the plugin identifier format (langgenius/gemini/google). Also migrates reranking_provider_name within the retrieval_model JSON column in datasets.

transform-datasource-credentials — converts legacy OAuth/API-key datasource credentials (Notion, Firecrawl, Jina) into the plugin-based DatasourceProvider model. Installs the corresponding marketplace plugin if not already present.

Plugin Installation / Extraction (Bulk Migration)#

These commands coordinate bulk install during initial plugin system adoption :

CommandWhat it does
extract-pluginsScans every tenant and writes installed plugin IDs to a JSONL file (multi-threaded)
extract-unique-identifiersResolves plugin IDs to latest marketplace package identifiers
install-pluginsDownloads + installs resolved packages for each tenant in batches of ≤64
install-rag-pipeline-pluginsSame flow but for RAG pipeline plugins via PluginService.install_from_marketplace_pkg()

PluginMigration.install_plugins() uses a temporary fake tenant to batch-download all packages before per-tenant install, reducing network round trips.


Upgrade Runbook (1.15.0+)#

After upgrading Dify to 1.15.0 or later:

  1. Run schema migration: docker compose exec api flask upgrade-db
  2. Run auto-upgrade backfill: docker compose exec api flask backfill-plugin-auto-upgrade
  3. Verify the plugin daemon version matches the Dify release (e.g., 0.6.3-local for 1.15.0).
  4. Remove stale version directories from PLUGIN_INSTALLED_PATH once plugins are confirmed working.

See Plugin Database Integrity for failure patterns (version reconciliation, model_type enum mismatch, duplicate plugin_unique_identifier) and their SQL fixes.