Database Migrations#
RAGFlow's database migration system handles schema and data transformations for model provider tables, introduced as part of the v0.27.0 model provider refactor . Migrations run automatically at Docker container startup via a shared shell orchestrator.
Entry Points#
Two Docker startup scripts delegate migrations to the same shared script, tools/scripts/run_migrations.sh :
docker/launch_backend_service.sh— callsrun_migrations.shunconditionally insiderun_mysql_migrations()wheneverSTART_RAGFLOW=1, afterensure_db_initcompletes .docker/entrypoint.sh— callsrun_migrations.shonly when the--init-model-provider-tablesflag is passed (i.e.,INIT_MODEL_PROVIDER_TABLES=1) .
Both entrypoints previously contained inline mysql_migration.py calls; centralizing them in run_migrations.sh ensures version consistency across deployment paths .
Migration Orchestrator: run_migrations.sh#
tools/scripts/run_migrations.sh runs migrations in two ordered phases :
| Phase | --database-version | Stages |
|---|---|---|
| 1 | v0.26.0 | tenant_model_provider, tenant_model_instance, tenant_model, model_id_config |
| 2 | v0.27.0.dev0 | tenant_model_seeding, model_type_merge, tenant_model_id_migration |
Each phase passes --execute --mark-database-version-on-success to mysql_migration.py. If the stored database version already meets or exceeds the target, the phase is skipped entirely.
Core Script: mysql_migration.py#
tools/scripts/mysql_migration.py is the migration engine. Key internals:
- Stage registry — A
MIGRATION_STAGESdict maps stage names to classes . Adding a new stage requires only a new class and a dict entry. MigrationStagebase class — All stages implementcheck()(returns bool: is migration needed?) andexecute()(performs the work, returns(rows_affected, tables_operated)) .- Version gating —
should_skip_migration()reads the version stored under key"mysql_migration.database.version"in thesystem_settingstable; if the stored version ≥ target, all stages in that phase are skipped . - Version persistence — On success,
db.set_database_version()writes the target version back tosystem_settings. - Execution modes —
--execute(full run),--dry-run(check only), and implicit create-table-only (schema without data migration) are supported.
Migration Stages Reference#
All seven registered stages :
| Stage name | What it does |
|---|---|
tenant_model_provider | Migrates tenant_llm.llm_factory → tenant_model_provider.provider_name |
tenant_model_instance | Migrates tenant_llm → tenant_model_instance with provider ID lookup |
tenant_model | Migrates tenant_llm → tenant_model table |
model_id_config | Normalizes stored model IDs from model@provider → model@default@provider |
tenant_model_seeding | Seeds tenant_model from conf/llm_factories.json for all provider instances |
model_type_merge | Merges tenant_model rows by (provider_id, instance_id, model_name), converting model_type strings to binary integer flags (1=chat, 2=embedding, 4=speech2text, 8=image2text, 16=rerank, 32=tts, 64=ocr) |
tenant_model_id_migration | Populates tenant_*_id FK columns in tenant, knowledgebase, dialog, memory with tenant_model.id UUID values |
The model_type_merge stage introduced binary bit-flag encoding for model types, replacing per-row string values to allow a single record to represent multi-capability models .
Schema Changes (v0.27.0)#
Driven by tenant_model_id_migration and reflected in api/db/db_models.py :
tenant.tenant_llm_id,tenant_embd_id,tenant_asr_id,tenant_img2txt_id,tenant_rerank_id,tenant_tts_id— converted fromIntegerFieldtoCharField(max_length=32)(UUID references totenant_model.id); newtenant_ocr_idcolumn added.knowledgebase.tenant_embd_id,dialog.tenant_llm_id,dialog.tenant_rerank_id,memory.tenant_embd_id,memory.tenant_llm_id— sameIntegerField→VARCHAR(32)conversion.
Adding a New Migration Stage#
- Subclass
MigrationStageintools/scripts/mysql_migration.pyand implementcheck()andexecute(). - Register the new class in
MIGRATION_STAGES. - Add the stage name to the appropriate phase in
tools/scripts/run_migrations.sh, with a new--database-versiontarget if it marks a new release boundary.