跳到主要内容

数据库迁移

概述

Open WebUI 在启动时会自动运行数据库迁移。通常极少需要手动迁移,只有在特定的失败场景或维护情况下才应执行手动迁移。

何时需要手动迁移

仅在以下情况下,您才需要进行手动迁移:

  • Open WebUI 在启动时的日志中显示了特定的迁移错误
  • 您正在进行离线数据库维护
  • 版本升级后自动迁移失败
  • 您正在不同的数据库类型之间迁移(SQLite ↔ PostgreSQL)
  • 开发者指示您手动运行迁移
快捷修复:升级后的迁移错误

"No such table" — 您的迁移未成功应用。请进入您的容器,设置所需的环境变量(参见步骤 2),然后运行 alembic upgrade head。参见详细细节

"Table already exists" — 上一次迁移只完成了一半。您需要标记该部分已应用的迁移,然后进行升级。参见详细细节

大版本跳跃后的多重错误(例如:“duplicate column” 之后出现 “table already exists”,然后是 “no such column”) — 您的数据库在多个迁移之间处于部分迁移状态。您需要逐个运行迁移。参见详细细节

关键警告

如果操作不当,手动迁移可能会损坏您的数据库。在继续之前,请务必创建一个经确认有效的备份。

前置条件核对清单

在开始之前,请确保您拥有:

  • 对您的 Open WebUI 部署拥有 Root/管理员访问权限
  • 确认数据库文件位置(在 Docker 中默认为 /app/backend/data/webui.db
  • 完全停止 Open WebUI(没有正在运行的进程)
  • 创建并确认了备份(参见下文)
  • 可以访问运行 Open WebUI 的容器或 Python 环境
请先停止所有进程

在 Open WebUI 处于活跃状态时,无法运行数据库迁移。您必须在尝试手动迁移之前停止所有 Open WebUI 进程。

步骤 1:创建并验证备份

备份您的数据库

# 首先查找您的数据库挂载位置
docker inspect open-webui | grep -A 5 Mounts

# 创建带有时间戳的备份
cp /path/to/webui.db /path/to/webui.db.backup.$(date +%Y%m%d_%H%M%S)

验证备份的完整性

关键: 在继续之前,测试您的备份文件是否可读。

# 测试备份是否能正常打开并查询
sqlite3 /path/to/webui.db.backup "SELECT count(*) FROM user;"

# 验证 Schema 结构是否一致
sqlite3 /path/to/webui.db ".schema" > current-schema.sql
sqlite3 /path/to/webui.db.backup ".schema" > backup-schema.sql
diff current-schema.sql backup-schema.sql
备份存储

将备份保存在与您数据库不同的磁盘或卷上,以防范磁盘损坏的风险。

步骤 2:诊断当前状态

在尝试任何修复之前,收集关于您数据库状态的信息。

访问您的环境

# 首先停止 Open WebUI
docker stop open-webui

# 进入容器进行诊断
docker run --rm -it \
  -v open-webui:/app/backend/data \
  --entrypoint /bin/bash \
  ghcr.io/open-webui/open-webui:main
确认您的位置

进入容器后,检查您当前的路径:

pwd

Alembic 的配置文件位于 /app/backend/open_webui/alembic.ini。无论您的初始目录是什么,请导航到该路径。

导航到包含 alembic.ini 的目录,并配置必需的环境变量:

# 首先,确认您当前所在的位置
pwd

# 导航到 Alembic 目录(如果当前路径不同,请调整路径)
cd /app/backend/open_webui  # Docker
# 或者
cd /path/to/open-webui/backend/open_webui  # 本地安装

# 验证当前目录下存在 alembic.ini
ls -la alembic.ini

设置必需的环境变量

# 必需项:Database URL
# 针对 SQLite(使用 4 个斜杠表示绝对路径)
export DATABASE_URL="sqlite:////app/backend/data/webui.db"

# 针对 PostgreSQL
export DATABASE_URL="postgresql://user:password@localhost:5432/open_webui_db"

# 必需项:WEBUI_SECRET_KEY
# 从 backend 目录(注意不是 data 目录)下的现有文件中获取密钥
export WEBUI_SECRET_KEY=$(cat /app/backend/.webui_secret_key)

# 如果上述读取失败,尝试读取 data 目录下的密钥文件
# export WEBUI_SECRET_KEY=$(cat /app/backend/data/.webui_secret_key)

# 如果这两个文件都不存在,则手动生成一个
# export WEBUI_SECRET_KEY=$(python3 -c "import secrets; print(secrets.token_hex(32))")
# echo $WEBUI_SECRET_KEY > /app/backend/.webui_secret_key

# 验证这两项已成功设置
echo "DATABASE_URL: $DATABASE_URL"
echo "WEBUI_SECRET_KEY: ${WEBUI_SECRET_KEY:0:10}..."
两个变量均属必需

如果缺失 WEBUI_SECRET_KEY,Alembic 命令会报 Required environment variable not found 错误。Open WebUI 的代码导入了 env.py,该文件会在 Alembic 甚至还未连接数据库之前验证此变量是否存在。

SQLite 的路径语法
  • sqlite:////app/... = 总共 4 个斜杠(绝对路径:sqlite:// + / + /app/...
  • sqlite:///../data/... = 总共 3 个斜杠(相对路径)

运行诊断命令

执行以下只读诊断命令:

# 检查当前的迁移版本
alembic current -v

# 检查目标(最新)版本
alembic heads

# 列出所有迁移历史记录
alembic history

# 显示待挂起的迁移(预览即将应用的内容)
alembic upgrade head --sql | head -30

# 检查是否有分支存在(通常指示有问题)
alembic branches

预期输出:

# alembic current 应该输出类似于:
ae1027a6acf (head)

# 如果您看到了多个 heads 或分支(branches),表示您的迁移历史记录存在问题
理解输出含义
  • alembic current = 您的数据库目前所认为的当前版本
  • alembic heads = 代码所期望的最新版本
  • alembic upgrade head --sql = 预览即将执行的 SQL(不会对数据库应用任何修改)
  • 如果 current 的版本早于 heads,表示您有待应用的挂起迁移
  • 如果 current 等于 heads,表示您的数据库已是最新
检查实际数据库中的表

验证您数据库中的实际内容:

sqlite3 /app/backend/data/webui.db ".tables"
sqlite3 /app/backend/data/webui.db "SELECT * FROM alembic_version;"

步骤 3:应用迁移

标准升级(最常见)

如果诊断结果显示您有待挂起的迁移(current < heads),请升级到最新版:

# 确保您身处正确的目录下
cd /app/backend/open_webui

# 运行升级
alembic upgrade head

密切留意这些输出内容:

INFO  [alembic.runtime.migration] Context impl SQLiteImpl.
INFO  [alembic.runtime.migration] Will assume non-transactional DDL.
# highlight-next-line
INFO  [alembic.runtime.migration] Running upgrade abc123 -> def456, add_new_column
"Will assume non-transactional DDL"

这是 SQLite 的正常提示信息,而非错误。SQLite 不支持对 Schema 修改进行回滚,因此迁移在没有事务保护的情况下运行。

如果进程在此信息后看似卡住,请耐心等待 2-3 分钟——某些迁移需要耗费一些时间,尤其是:

  • 向大型表(100万+行:耗时 1-5 分钟)添加索引的迁移
  • 包含数据转换的迁移(10万+行:耗时 30 秒至数分钟)
  • 重建表结构的迁移(SQLite 不支持所有的 ALTER 操作)

针对超大型数据库(1000万+行),建议在维护时间窗口内运行迁移,并在另一个终端中通过 sqlite3 /path/to/webui.db ".tables" 监控进度。

升级到指定版本

如果您需要将迁移应用到特定的某一步骤:

# 首先列出所有可用的版本
alembic history

# 升级到指定的 Revision ID
alembic upgrade ae1027a6acf

降级(回滚)

数据丢失风险

如果迁移中删除了列或表,降级操作可能会导致永久性数据丢失。请在完全了解后果的情况下才进行降级。

# 降级一个版本
alembic downgrade -1

# 降级到指定版本
alembic downgrade <revision_id>

# 终极选项:撤销所有的迁移(极少需要)
alembic downgrade base

步骤 4:验证迁移成功

运行迁移后,确认一切都已正确无误:

# 验证当前版本是否与预期一致
alembic current
# 应该显示 (head) 表示您处于最新状态
# 示例:ae1027a6acf (head)

# 确认已无挂起的迁移
alembic upgrade head --sql | head -20
# 如果输出中只包含注释或为空,表示您已经是最新版本

# 验证关键的数据表是否存在 (SQLite)
sqlite3 /app/backend/data/webui.db ".tables" | grep -E "user|chat|model"
# 应该在列出的表中看到 user、chat、model 等表

# 运行一个简单的查询,确保 Schema 完好无损
sqlite3 /app/backend/data/webui.db "SELECT COUNT(*) FROM user;"
# 应该返回一个数字,而不是报错信息

测试应用启动

# 退出诊断容器
exit

# 正常启动 Open WebUI
docker start open-webui

# 监控日志以获取迁移确认信息
docker logs -f open-webui

# 查看是否成功启动,然后在浏览器中进行测试
# 导航至 http://localhost:8080 并验证登录页面是否能正常加载

启动成功的日志示例:

INFO:     [db] Database initialization complete
INFO: [main] Open WebUI starting on http://0.0.0.0:8080

启动后的冒烟测试(Smoke Test):

  • 能否正常访问登录页面
  • 能否使用现有的凭据成功登录
  • 能否正常查看历史对话记录
  • 浏览器 JavaScript 控制台无报错

故障排除

"No such table"(无此数据表)错误

症状: Open WebUI 在启动时崩溃,并抛出如下错误:

sqlalchemy.exc.OperationalError: (sqlite3.OperationalError) no such table: access_grant

或者其他指向缺失的某些数据表(例如 messagechannel)的类似错误。

原因: 一项或多项 Alembic 迁移未能成功应用。这通常在以下情况下发生:

  • 在自动升级过程中迁移静默失败(Open WebUI 记录了错误但继续尝试启动)
  • 迁移在运行过程中被意外中断
  • 环境中设置了 ENABLE_DB_MIGRATIONS=False(禁用了启动时的自动迁移功能)
  • 多个 Worker 或副本(replicas)同时尝试运行迁移
ENABLE_DB_MIGRATIONS 无法直接修复此问题

设置 ENABLE_DB_MIGRATIONS=True(默认值)仅告诉 Open WebUI 在下一次启动时尝试自动迁移。它无法溯及既往地修复先前已失败或已被跳过的迁移。如果您的数据库已处于损坏状态,您必须手动应用这些迁移。

解决方案:

遵循步骤 2访问您的环境,然后运行:

cd /app/backend/open_webui  # Docker
alembic upgrade head

这将应用所有挂起的迁移,包括创建任何缺失的表。成功迁移后,正常重启 Open WebUI 即可。

如果您在手动迁移过程中看到其他错误(如 “table already exists”),请查阅下面针对特定错误信息的其他故障排除章节。

"Table Already Exists"(数据表已存在)错误

症状: 运行 alembic upgrade head 时失败,并提示:

sqlalchemy.exc.OperationalError: (sqlite3.OperationalError) table chat_message already exists

或者针对其他数据表(如 access_grant)的类似错误。

原因: 先前的某次迁移只完成了一部分——数据表已在数据库中创建,但 Alembic 的版本追踪尚未更新(通常是因为迁移在创建表之后、写入历史数据回填的步骤被意外中断)。Alembic 依然认为该迁移未应用,因此会尝试重新创建该表。

诊断步骤:

# 检查 Alembic 所认为的当前版本
alembic current
# 示例输出:374d2f66af06 (head)

# 检查下一个迁移是什么
alembic history
# 寻找紧跟在您当前版本之后的下一个迁移
# 示例:374d2f66af06 -> 8452d01d26d7, Add chat_message table

解决方案:

有三种方式可以解决该问题,按从“最安全”到“可能丢失数据”的顺序排列如下:

将您的数据库还原为在步骤 1中创建的干净备份,然后在此备份上干净地运行 alembic upgrade head。这能确保完整的迁移过程(包括所有的数据回填)正确完成。

选项 2:删除表并重新运行

如果您没有备份,可以删除这个已部分创建的表,并让迁移重新从头运行。在执行此操作前,请先验证其是否安全:

# 1. 确认迁移是不完整的(当前版本 revision 应该在报错版本的【之前】)
alembic current

# 2. 检查该表目前有多少数据(如果包含数据的话)
# SQLite:
sqlite3 /app/backend/data/webui.db "SELECT COUNT(*) FROM <table_name>;"
# PostgreSQL:
psql $DATABASE_URL -c "SELECT COUNT(*) FROM <table_name>;"

# 3. 打开迁移脚本文件,验证源数据依然存在
#    通过 Revision ID 寻找对应文件:
ls migrations/versions/ | grep <revision_id>
#    阅读它并寻找它是从哪些源列/源表中拷贝数据的。
#    然后验证这些源列在您的数据库中依然存在。

一旦您确认迁移并未完成且源数据完好无损,即可删除该表并重新运行:

# SQLite:
sqlite3 /app/backend/data/webui.db "DROP TABLE <table_name>;"

# PostgreSQL:
psql $DATABASE_URL -c "DROP TABLE <table_name>;"

# 重新运行迁移
alembic upgrade head
请先检查迁移脚本文件

仅当迁移是拷贝(copy)数据(即将旧列的数据复制到新表中,且原始数据保持完整)时,这才是安全的。请先打开迁移脚本文件,确认它使用的是 INSERT INTO ... SELECT FROM 或类似逻辑——而修改或删除源数据的破坏性操作。如果您不确定,请使用选项 1。

选项 3:标记越过它(最后手段)

如果上述两项方案均不可行,您可以告诉 Alembic 直接跳过这个卡住的迁移:

# 在不实际运行 SQL 的情况下将该迁移标记为已应用
alembic stamp <revision_id>

# 继续进行剩余的迁移
alembic upgrade head
这将跳过数据回填步骤

标记(stamping)会把该步迁移设为已完成,但会跳过剩余的其他步骤,如将历史数据拷贝到新表中。您的旧数据并未被删除——它依然存在于原始的列中——但应用程序此后可能不会再去读取这些旧列。某些功能可能会在缺失历史数据的情况下工作,而另一些功能可能会完全丢失设置。

如果 alembic upgrade head 随后在另一个不同的迁移上再次失败并报“数据表已存在”错误,请对每个卡住的迁移重复此过程。

遇到多个卡住的迁移?

如果您在每次重试时都遇到不同的错误——“duplicate column” 之后是 “table already exists”,然后是 “no such column”——表示您的数据库结构在多个步骤之间处于不完整的混合状态。这通常在大版本跳跃升级(例如 0.7.x → 0.8.x)时发生。请参阅下方的大版本跳跃升级后的多次失败获取逐步恢复工作流。

大版本跳跃升级后的多次失败

症状: 在跨越多个版本(例如 0.7.x → 0.8.x)升级后,Open WebUI 在启动时崩溃并提示 “no such column” 错误(例如 no such column: user.scim)。运行 alembic upgrade head 会在较早的某次迁移上因 “duplicate column” 或 “already exists” 错误而失败。修复该问题后,又在下一个迁移中暴露出另一个错误,以此类推。

原因: 当 Open WebUI 在大版本跳跃后启动时,它会尝试按顺序运行所有挂起的迁移。如果链条中的任何一次迁移在途中部分失败(常见原因:没有设置默认值的 SQLite NOT NULL 约束、被中断的进程、大型数据回填时的内存超限等),由于 SQLite 迁移是非事务性的,部分 Schema 的修改就会遗留下来,而 alembic_version 并不会向前推进。每次重启都会重新尝试运行这次失败的迁移,导致在已应用的修改上崩溃,并且永远无法触达后续的迁移。这会导致您的数据库 Schema 变成一个“拼贴画”:应用了早期的一些修改,但后续迁移的修改全都没有生效。

诊断步骤:

# 检查 Alembic 所认为的当前版本
alembic current
# 示例:37f288994c47  (落后于 head 数个版本)

# 检查最新的 head 是什么
alembic heads
# 示例:b2c3d4e5f6a7  (head)

# 列出当前版本与最新版本之间的完整链条
alembic history

# 检查当前 Revision 下本不该存在的 Schema 元素
# (存在这些元素说明后续的一些迁移已经被部分应用了)
sqlite3 /app/backend/data/webui.db "PRAGMA table_info(channel_member);" | grep status
sqlite3 /app/backend/data/webui.db "SELECT name FROM sqlite_master WHERE type='table' AND name='chat_message';"

如果 alembic current 显示的是一个旧的版本,但您已经可以在数据库中看到后续迁移才应该加入的表或列,说明您遇到了已部分应用的迁移链。

解决方案 —— 每次前进一步:

基本思路:单独升级到每一个具体的 Revision 版本。如果成功,则继续下一步。如果因 “duplicate column” 或 “already exists” 失败,则标记越过它(stamp)。如果遇到其他类型的错误,停止操作并进行排查。

# 获取有序的待挂起迁移列表
alembic history

# 尝试应用第一个挂起的迁移
alembic upgrade <first_pending_revision>

如果成功应用:

# 继续升级到下一个
alembic upgrade <next_revision>

如果因 “duplicate column name” 或 “already exists” 失败:

# 说明该迁移的 Schema 已经在先前的部分运行中应用了。
# 标记越过它以推进 alembic_version,而无需重新运行 SQL 语句。
alembic stamp <that_revision>

# 继续执行下一个迁移
alembic upgrade <next_revision>

重复此步骤直到触达 head。

此时使用标记(stamping)安全吗?

当您确认迁移的 Schema 更改已经存在于数据库中时,标记是安全的——“已经存在”或“重复列”错误本身就已经印证了这一点。这与盲目地运行 alembic stamp head 是完全不同的,因为后者会不管 SQL 语句是否应用而跳过所有挂起的迁移。

特殊情况 —— 包含数据回填的迁移:

某些迁移不仅改变 Schema,还会将数据从旧表拷贝到新表中(例如,chat_message 迁移会将 chat 表的 JSON 列中的数据回填进去)。如果该迁移的建表步骤成功但回填步骤被意外中断:

# 检查表中是否已经包含数据
sqlite3 /app/backend/data/webui.db "SELECT COUNT(*) FROM <table_name>;"

如果数量大于 0,说明回填很可能已完成——请标记越过它。如果数量为 0,您有两种选择:

  1. 删除表并重新运行(保留数据,但在大型数据库上回填可能需要耗费较长时间):

    sqlite3 /app/backend/data/webui.db "DROP TABLE <table_name>;"
    alembic upgrade <that_revision>
  2. 直接标记越过它(速度快,但会跳过数据回填——某些依赖于新表的功能可能会在历史数据中出现空白):

    alembic stamp <that_revision>

验证并启动:

alembic current
# 应该输出:<latest_revision> (head)

随后退出容器并正常启动:

exit
docker start open-webui
docker logs -f open-webui
遇到意外错误请立即停止

标记并继续(stamp-and-continue)的方法仅适用于 “already exists” 和 “duplicate column” 错误(这可以反向证明迁移已经生效)。如果迁移因为其他错误(如数据类型不匹配、外键约束冲突、或者与 Schema 无关的 Python 报错)而失败,请千万不要标记越过它。请将完整错误日志提交至 GitHub Issue 或 Discord 中。

"Required environment variable not found"(未找到必需的环境变量)

原因: 缺失 WEBUI_SECRET_KEY 环境变量。

解决方案: 按照步骤 2:设置必需的环境变量中描述的方法设置 WEBUI_SECRET_KEY 环境变量,然后重新运行您的 Alembic 命令。

为什么会发生此报错

Open WebUI 的 env.py 文件导入了模型,模型又导入了 open_webui.env,而该文件会验证 WEBUI_SECRET_KEY 是否存在。如果没有它,Python 将会在 Alembic 甚至还未连接数据库之前崩溃。

"No config file 'alembic.ini' found"(未找到 alembic.ini 配置文件)

原因: 您身处错误的目录路径下。

解决方案:

# 如果容器名不是 'open-webui',请先查明容器名
docker ps

# 寻找 alembic.ini 的位置
find /app -name "alembic.ini" 2>/dev/null  # Docker
find . -name "alembic.ini"  # 本地安装

# 导航至该目录路径下
cd /app/backend/open_webui  # 最常见的路径

# 验证您确实进入了正确的路径
ls -la alembic.ini

"Target database is not up to date"(目标数据库不是最新版本)

原因: 您的数据库版本与代码预期的 Schema 不匹配。

诊断步骤:

# 检查数据库当前所记录的版本
alembic current

# 检查代码预期的版本
alembic heads

# 对比两者的输出

解决方案取决于诊断结果:

场景: alembic current 显示的版本旧于 alembic heads

修复方法: 您只需要正常应用挂起的迁移即可。

alembic upgrade head
千万不要使用 "alembic stamp head" 来作为修复手段

您可能会看到某些建议通过运行 alembic stamp head 来“修复”版本不匹配的问题。这非常危险。

alembic stamp head 相当于告诉 Alembic “假装所有迁移都已应用”,而不去实际运行任何 SQL 语句。这会导致永久性的数据库损坏,因为 Alembic 认为您的数据库 Schema 是最新的,而实际上并非如此。

仅在以下情况下,使用 alembic stamp <specific_revision> 才是安全的:

  • 您已证实迁移的 Schema 更改已经存在于数据库中(例如,通过 “already exists” 或 “duplicate column” 报错加以印证)——参见大版本跳跃升级后的多次失败
  • 您手动使用 create_all() 创建了所有的表,且需要将它们标记为已迁移
  • 您是开发者,正在初始化一个符合当前最新 Schema 的全新数据库
  • 您从其他系统导入了数据库备份,且需要将其标记在正确的 Revision 版本上
  • 您已通过原生 SQL 手动应用了迁移,且需要更新迁移追踪信息

千万不要通过 alembic stamp head 来一次性跳过所有挂起的迁移。

进程在 "Will assume non-transactional DDL" 后挂起

理解此输出:不是错误信息。这只是一个提示。SQLite 不支持事务性 DDL,因此 Alembic 在警告您迁移操作无法自动回滚。

如果进程确实卡死挂起:

某些迁移(特别是添加索引或修改大型表的迁移)需要耗费几分钟的时间。

行动: 在判定为卡住之前,请先耐心等待 3-5 分钟。

自动生成检测到被移除的表

症状: 您运行了 alembic revision --autogenerate,且提示需要 Drop 现有的数据表。

别运行 Autogenerate 命令

普通用户永远不需要运行 alembic revision --autogenerate 该命令是供开发者用于编写新的迁移脚本文件的,而不是供用户应用现有迁移的。

您真正需要使用的命令是 alembic upgrade head(不带 revision--autogenerate)。

如果您不小心创建了错误的自动生成迁移文件:

# 列出迁移脚本文件
ls -la /app/backend/open_webui/migrations/versions/

# 删除错误的自动生成文件(最新的那个文件)
rm /app/backend/open_webui/migrations/versions/<newest_timestamp>_*.py

# 还原到已知的正常状态
git checkout /app/backend/open_webui/migrations/  # 如果使用了 Git 的话

技术背景: 发生“自动生成检测到被移除的表”问题,是因为 Open WebUI 的 Alembic 元数据配置中并未导入全部的模型定义。这会导致自动生成命令与不完整的元数据对比,误以为表应该被删掉。这是开发者层面的问题,完全不影响用户运行 alembic upgrade

PostgreSQL 外键错误

仅限 PostgreSQL

本排障指导仅适用于 PostgreSQL 数据库。SQLite 处理外键约束的机制有所不同。

症状: 出现类似于 psycopg2.errors.InvalidForeignKey: there is no unique constraint matching given keys for referenced table "user" 的报错信息。

原因: PostgreSQL 要求显式的主键约束,而在较旧的版本 Schema 中可能遗漏了这些约束。

针对 PostgreSQL 的解决方案:

-- 连接到您的 PostgreSQL 数据库
psql -h localhost -U your_user -d open_webui_db

-- 添加缺失的主键约束(PostgreSQL 语法)
ALTER TABLE public."user" ADD CONSTRAINT user_pk PRIMARY KEY (id);

-- 验证主键是否已被成功添加
\d+ public."user"

注意: public. Schema 前缀和双引号括起来的 "user" 标识符是 PostgreSQL 特有的。此 SQL 语句在 SQLite 或 MySQL 上无法工作。

重复列名错误

关键性问题

重复列名错误表明 Schema 结构已经损坏,通常是由于先前迁移失败或手动修改数据库造成的。这需要谨慎的手动干预。

症状: 迁移失败并提示如下错误:

sqlite3.OperationalError: duplicate column name: message.reply_to_id

或者在启动 Open WebUI 时:

UNIQUE constraint failed: alembic_version.version_num

原因:

  • 先前的迁移部分完成,遗留了重复的列
  • 数据库曾被手动修改过
  • 迁移在执行中途被中断
  • 直接跨越太多版本进行升级(跳过了中间的迁移)

诊断步骤:

# 列出 message 表中的所有列信息
sqlite3 /app/backend/data/webui.db "PRAGMA table_info(message);"

# 在输出中寻找是否有重复的列名
# 示例问题输出:
# 10|reply_to_id|TEXT|0||0
# 15|reply_to_id|TEXT|0||0  <- 重复的列!

解决方案 - 手动删除重复的列:

数据丢失风险

删除列有数据丢失的风险。在继续操作之前,请务必备份您的数据库

# 1. 务必先备份数据库!
cp /app/backend/data/webui.db /app/backend/data/webui.db.pre-fix

# 2. 进入 SQLite
sqlite3 /app/backend/data/webui.db

# 3. 检查当前的表结构信息
PRAGMA table_info(message);

# 4. SQLite 不直接支持 DROP COLUMN - 必须重建表结构
# 首先,获取 CREATE TABLE 的建表语句
.schema message

# 5. 创建一个不带重复列的新表
-- 拷贝原建表语句,但删除重复的那个列的定义
-- 示例(请根据您的实际 Schema 进行微调):
CREATE TABLE message_new (
    id TEXT PRIMARY KEY,
    content TEXT,
    role TEXT,
    -- ... 其他列 ...
    reply_to_id TEXT,  -- 仅保留这一个实例
    -- ... 剩余的列 ...
    FOREIGN KEY (reply_to_id) REFERENCES message(id)
);

# 6. 将数据从旧表拷贝到新表中
INSERT INTO message_new SELECT * FROM message;

# 7. 删除旧表
DROP TABLE message;

# 8. 重命名新表
ALTER TABLE message_new RENAME TO message;

# 9. 退出 SQLite
.quit

# 10. 验证修复是否生效
sqlite3 /app/backend/data/webui.db "PRAGMA table_info(message);"

# 11. 重新尝试迁移
cd /app/backend/open_webui
alembic upgrade head

替代方案 —— 如果已知重复列,则可采用更简便的方法:

# 仅在列确实完全重复,且 SQLite 的表重建能够正确处理时,此方法才适用

sqlite3 /app/backend/data/webui.db <<EOF
-- 创建包含去重数据的临时表
CREATE TABLE message_temp AS SELECT DISTINCT * FROM message;

-- 删除原表
DROP TABLE message;

-- 重新以正确的 Schema 建表(最初从 .schema message 获取建表语句)
-- 然后将数据重新拷贝回去
EOF
何时应寻求帮助

如果您对 SQL 不熟悉,或者不确定哪个列才是重复的,请立即停止操作并寻求帮助,可以通过 Open WebUI 的 GitHub Issue 或 Discord 提问。请提供:

  • PRAGMA table_info(message); 的输出结果
  • 完整的迁移报错信息
  • 您的 Open WebUI 版本历史(升级前和升级后的版本)

预防措施:

  • 升级时永远不要跨越太多大版本(例如不要直接从 v0.1.x 跳跃升级到 v0.4.x)
  • 升级前务必备份数据库
  • 先在您数据库的副本上测试升级操作
  • 仔细审查对应升级路径的迁移脚本

Peewee 到 Alembic 过渡问题

背景: 较早的 Open WebUI 版本(v0.4.x 之前)使用的是 Peewee 迁移。而当前的新版本则使用 Alembic。

症状:

  • 同时存在 migratehistoryalembic_version 数据表
  • 提示关于“迁移已应用”的报错信息

自动发生的过渡逻辑:

  1. Open WebUI 的 internal/db.py 会首先通过 handle_peewee_migration() 运行旧的 Peewee 迁移
  2. 随后 config.py 会通过 run_migrations() 运行 Alembic 迁移
  3. 这两套系统理论上应该是能够透明且协同工作的

如果自动过渡失败:

# 检查旧的 Peewee 迁移历史记录是否存在
sqlite3 /app/backend/data/webui.db "SELECT * FROM migratehistory;" 2>/dev/null

# 如果 Peewee 迁移历史存在,确保它们已完成
# 随后手动运行 Alembic 迁移
cd /app/backend/open_webui
alembic upgrade head
提示

如果从非常古老的 Open WebUI 版本(< v0.3.x)升级,建议配置全新的安装并进行数据导出/导入,而不是尝试跨越多个大版本去生硬迁移旧的数据库结构。


高级操作

生产与多服务器部署

滚动更新可能会引发失败

在多服务器部署中,在滚动更新期间同时运行不同的代码版本可能会引发错误——因为新代码期望的 Schema 尚未被应用,或者旧代码与新 Schema 不兼容。

推荐的部署策略:

在部署新的应用程序代码之前,将数据库迁移作为一次性的任务(Job)来运行:

# 1. 运行迁移任务
kubectl apply -f migration-job.yaml

# 2. 等待任务完成
kubectl wait --for=condition=complete job/openwebui-migration

# 3. 部署新版本的应用程序
kubectl rollout restart deployment/openwebui

这可以确保在任何新版代码运行之前,数据库结构已被成功更新。

生成 SQL 而不应用它

为了进行审查或审计,可以单独生成即将被执行的 SQL 语句:

# 生成待挂起迁移的 SQL 语句
alembic upgrade head --sql > /tmp/migration-plan.sql

# 审查即将被应用的内容
cat /tmp/migration-plan.sql

使用场景:

  • 企业环境下的 DBA 审查
  • 深入了解将要发生哪些改变
  • 调试迁移中的棘手问题
  • 在受限的环境中手动应用迁移
何时需要使用此项

这是针对 DBA 或 DevOps 工程师的高级功能。普通用户应当直接运行 alembic upgrade head

离线迁移(无网络环境)

如果您的数据库服务器处于离线或隔离网段中:

# 1. 在开发机上生成 SQL 脚本
alembic upgrade head --sql > upgrade-to-head.sql

# 2. 将 SQL 文件传输至生产环境服务器
scp upgrade-to-head.sql production-server:/tmp/

# 3. 在生产服务器上,手动应用 SQL
sqlite3 /app/backend/data/webui.db < /tmp/upgrade-to-head.sql

# 4. 手动更新 alembic_version 表的版本追踪信息
sqlite3 /app/backend/data/webui.db \
  "UPDATE alembic_version SET version_num='<target_revision>';"
手动更新 alembic_version 的风险

仅在您确实应用了相应迁移的前提下,才去手动修改 alembic_version。欺骗 Alembic 会导致数据库结构陷入永久性的混乱。


恢复步骤

从失败的迁移中恢复

SQLite 不支持回滚

SQLite 的迁移是非事务性的。如果迁移在中途失败,您的数据库就会停留在只迁移了部分的混合状态下。唯一安全的恢复方式是直接恢复干净的备份。

部分迁移成功的常见症状:

  • 部分表存在,而其他表不匹配预期的 Schema
  • 外键约束报错
  • 遗漏了迁移本应该添加进去的列
  • 应用程序频繁报错提示缺失数据库字段

恢复步骤:

# 1. 立即停止 Open WebUI
docker stop open-webui

# 2. 校验备份文件的完整性
sqlite3 /path/to/webui.db.backup "PRAGMA integrity_check;"

# 3. 还原备份
cp /path/to/webui.db.backup /path/to/webui.db

# 4. 在重新尝试迁移前,先排查根本原因
docker logs open-webui > migration-failure-logs.txt

# 5. 根据日志解决问题后,再重新尝试迁移
别通过 "stamp" 来跳过未应用完的迁移

如果数据表 Schema 更改确实没有成功应用,切勿通过 alembic stamp 来将其标记为已完成。如果迁移是因为除 “already exists” 和 “duplicate column” 之外的其他报错而失败的,表明 Schema 确实缺失,此时强行 stamp 越过它会让您的数据库彻底损坏。关于何时适合使用 stamp,请参阅大版本跳跃升级后的多次失败

校验数据库完整性

在执行迁移的前后,验证您的数据库是否已损坏:

sqlite3 /app/backend/data/webui.db "PRAGMA integrity_check;"

# 应当输出:ok
# 如果输出其他任何内容,代表数据库文件已损坏

迁移后核对清单

数据库迁移成功后,核对以下内容:

  • alembic current 显示 (head) 表明目前已处于最新版本
  • Open WebUI 正常启动且无任何报错信息
  • 用户能够成功登录
  • 核心功能(对话交互、模型选择等)正常运转
  • 运行日志中无报错信息
  • 历史数据(用户、对话、模型等)完整无损
  • 稳定运行 1 周后,安全地将备份进行归档
保留近期备份

将重大迁移前的数据库备份保留至少 1-2 周。有些潜在的错误只有在经过几天特定流程的运转后才会逐渐暴露出来。

获取帮助

如果按照本指南操作后迁移依然报错:

收集以下诊断数据:

# 版本信息
docker logs open-webui 2>&1 | head -20 > diagnostics.txt

# 迁移状态
cd /app/backend/open_webui
alembic current -v >> diagnostics.txt
alembic history >> diagnostics.txt

# 数据库信息 (SQLite)
sqlite3 /app/backend/data/webui.db ".tables" >> diagnostics.txt
sqlite3 /app/backend/data/webui.db "SELECT * FROM alembic_version;" >> diagnostics.txt

# 完整的迁移报错信息
alembic upgrade head 2>&1 >> diagnostics.txt

获取支持的途径:

  1. Open WebUI Discord 社区
    • 寻找社区成员的实时支持
    • 分享您的报错信息与诊断文件
  2. 提供以下基本信息:
    • Open WebUI 版本号
    • 部署方式(Docker 或是本地安装)
    • 数据库类型(SQLite 或是 PostgreSQL)
    • alembic currentalembic history 的输出内容
    • 完整的错误日志
    • 出错时您正在进行的具体操作
备注

请不要将您的 webui.db 数据库文件直接公开分享——它包含了用户凭据及敏感的用户数据。只需分享上述整理出来的诊断文本即可。

This content is for informational purposes only and does not constitute a warranty, guarantee, or contractual commitment. Open WebUI is provided "as is." See your license for applicable terms.