别把青龙数据直接硬搬!我实测了一次青龙迁白虎,真正的坑在这 5 个地方
先说结论
从青龙面板迁移到白虎面板,最危险的做法不是“不会迁”,而是“以为复制目录就等于迁移成功”。这次我在一台隔离 Docker 实验环境里实际跑了一遍:新建青龙容器,造了两条环境变量、两个定时任务和几份脚本;再把它们迁到一套全新白虎面板里,最后用 API 和白虎页面截图核对。
最终可稳定迁移的是三类核心资产:
scripts脚本文件、环境变量、定时任务。真正需要转换的是:青龙的数字 ID 要变成白虎的字符串 ID;青龙任务里的task xxx.py要变成白虎能执行的命令;青龙的标签、启停状态、环境变量关系要写进白虎自己的表结构。迁移前必须备份白虎库,迁移时最好停掉白虎容器,迁移后必须做数量核对和页面验证。本文不展示任何真实内网地址、主机名、私有镜像仓库、真实 Cookie、真实 Token 或生产路径。截图来自实验环境,变量值也只使用脱敏样例。

读前导读:这篇不是“换个镜像名”的教程
青龙面板和白虎面板都属于“脚本调度面板”。你可以把它们理解成一个带网页界面的闹钟管家:每天几点跑哪个脚本、脚本要用哪些变量、跑完看哪些日志,都由它来记。问题是,这个管家手里有钥匙。它不只是展示页面,它会真正执行命令。
所以迁移不能像搬普通照片目录那样粗暴。普通照片搬家,复制过去能打开就行;调度面板搬家,像搬一个小型厨房:锅碗瓢盆是脚本,调料瓶是环境变量,菜谱时间表是定时任务,冰箱贴上的标签是分类。你如果只搬锅,不搬调料,菜做不出来;如果调料瓶名字变了,菜谱还指向旧瓶子,也会翻车。
白虎官方文档和 README 里能看到它支持 Docker 部署、/app/data 数据目录、/app/envs 运行环境目录,也支持青龙格式的仓库同步命令解析。青龙 README 则说明青龙常见 Docker 数据目录是 /ql/data。但这里有一个很容易误会的点:白虎支持青龙仓库命令格式,不等于白虎能直接读青龙数据库。 仓库命令像“快递单格式兼容”,数据库迁移则像“把旧房产证换成新系统里的房产证”,不是同一件事。
参考资料:
- QingLong GitHub: https://github.com/whyour/qinglong
- BaiHu Panel GitHub: https://github.com/engigu/baihu-panel
- BaiHu 文档:https://engigu.github.io/baihu-panel/
我实际做了什么实验
这次实验假设白虎已经是全新安装完成的状态。我没有动任何已有生产容器,而是只创建临时实验资源。实验目标很简单:让青龙里有可数、可对照、可脱敏的数据,然后迁到白虎里,看白虎是不是真的认。
实验里的青龙数据如下:
- 2 个环境变量:一个启用,一个禁用。
- 2 个定时任务:一个启用,一个禁用。
- 6 个脚本文件:包含实验脚本和青龙镜像自带的通知依赖脚本。
- 若干标签:例如
lab、python、shell、disabled。
迁移完成后的白虎仪表盘显示任务总数 2、环境变量 2。这个数字不是我手写在文章里的,是迁移后页面和 API 都能看到的结果。

问题表现:为什么不能直接复制 /ql/data
青龙的数据主要在 /ql/data。里面有数据库、脚本、日志、配置和依赖缓存。白虎的数据主要在 /app/data,运行环境另有 /app/envs。看起来都是“一个 data 目录”,但内部账本完全不同。
最直观的区别是 ID。青龙的 Envs 和 Crontabs 表用的是自增数字 ID,例如 1、2。白虎的 baihu_envs、baihu_tasks、baihu_data_relations 等表使用字符串 ID。这个区别像学校换了学籍系统:老系统里学生是一号、二号,新系统里每个人是更长的一串编号。你不能把“一号学生参加篮球队”直接搬过去,因为新系统里没有“一号学生”这个人。
第二个区别是命令格式。青龙里常见任务命令是:
task daily_check.py
白虎本地任务最终要能在自己的脚本目录下执行,所以我把它转换成:
python /app/data/scripts/qinglong-migrated/daily_check.py
Shell 脚本则转换成:
bash /app/data/scripts/qinglong-migrated/cleanup_cache.sh
第三个区别是关系表。青龙里环境变量更像全局变量,任务运行时通常能拿到;白虎有自己的 data_relations,标签和任务-变量关系都要按白虎方式写进去。否则页面上可能看着有任务、有变量,但筛选、标签、关联关系不完整。
迁移步骤:我按这个顺序跑通
第一步,盘点青龙数据。先确认青龙数据目录里至少有这些东西:
/ql/data/db/database.sqlite
/ql/data/scripts/
如果青龙使用的是别的持久化目录,就以你的 Docker Compose 绑定为准,不要照抄路径。生产迁移时还要先导出任务、变量、订阅、依赖清单,至少做到“迁移前知道有多少东西”。
第二步,确认白虎是全新安装完成。白虎启动后会生成自己的 SQLite 数据库,通常在:
/app/data/baihu.db
这里我遇到第一个坑:宿主机上没有 sqlite3 命令。这个问题不大,迁移脚本用 Python 标准库的 sqlite3 就能读写,不依赖宿主机额外安装数据库工具。
第三步,停掉白虎容器并备份白虎库。SQLite 正在被服务写入时,直接改库容易遇到锁、WAL 未落盘或读到旧状态的问题。我的迁移器会创建类似这样的备份文件:
baihu.db.before-ql2bh-<timestamp>.bak
备份这一步像搬家前先给新房拍照并保留钥匙。如果后面导入不对,可以退回去;没有备份,就只能边猜边修。
第四步,复制青龙脚本。脚本文件被放到白虎的:
/app/data/scripts/qinglong-migrated/
这样做有两个好处:一是不会覆盖白虎默认示例脚本;二是一眼就知道哪些文件来自青龙。迁移器同时会把文本脚本写入白虎的脚本记录表,方便在白虎“脚本编辑”页面看到。

第五步,转换环境变量。青龙 Envs.status=0 我按启用处理,非 0 按禁用处理;备注里追加 ql2bh 标记,方便重复执行时只清理上次迁移导入的数据,不碰白虎原有变量。

第六步,转换定时任务。任务名称、cron 表达式、启停状态、前置命令、后置命令、标签都会迁入;命令内容会从青龙的 task xxx 转成白虎能在 /app/data/scripts/qinglong-migrated/ 下执行的命令。
第七步,验证。我的验证不是只看命令退出码,而是三层验证:
- 数据库计数:环境变量、任务、脚本记录、任务-变量关系数量是否符合预期。
- API 计数:白虎
/api/v1/stats返回的任务和环境变量数量是否对。 - 页面截图:仪表盘、任务、变量、脚本页是否都能看到迁移结果。

这次遇到的坑和解决办法
第一个坑:白虎“支持青龙格式”不等于“支持青龙数据库直读”。解决办法是把迁移拆成资产迁移:脚本文件、变量记录、任务记录、标签关系。不要期待把青龙数据库文件改名成白虎数据库。
第二个坑:直接看 SQLite 主文件可能误判。白虎运行中会有 WAL 文件,主库文件大小或表状态不一定代表最终状态。解决办法是迁移前停白虎,迁移后重启,再通过 API 和页面确认。
第三个坑:宿主机没有 sqlite3。解决办法是用 Python 标准库。本文提供的脚本不要求宿主机安装 sqlite3 命令。
第四个坑:白虎 API 路径不是想当然的复数形式。我一开始查环境变量用了 /envs,结果是 404;源码和实测确认是 /api/v1/env。解决办法是以实际 API 和页面为准,不要猜路径。
第五个坑:多层 SSH 和 shell 引号很容易把脚本弄坏。我最开始把大段 Python 直接塞进 SSH 命令,SQL 字符串里的引号被外层 shell 吃掉,SQLite 报了语法错误。解决办法是迁移器独立成文件,上传或下载后执行,不把大脚本塞进一行命令。
一键脚本:Windows 11 / Ubuntu 26.04 / macOS 26
我把这次用到的通用迁移器和三端启动脚本放在文章附件里:
- 通用迁移器:/downloads/ql2bh-migration/ql2bh-migrate.py
- Windows 11 PowerShell:/downloads/ql2bh-migration/run-ql2bh-windows11.ps1
- Ubuntu 26.04 Bash:/downloads/ql2bh-migration/run-ql2bh-ubuntu2604.sh
- macOS 26 Zsh:/downloads/ql2bh-migration/run-ql2bh-macos26.zsh
这些脚本不依赖外部数据库或第三方云服务。它们假设青龙和白虎的数据目录在当前机器可访问,且白虎容器已经存在。脚本会用白虎容器当前镜像启动一个临时迁移环境,避免要求宿主机安装额外 Python 包。
Windows 11:
Set-Location C:\panel-migration
.\run-ql2bh-windows11.ps1 `
-QingLongData "D:\containers\qinglong\data" `
-BaiHuData "D:\containers\baihu\data" `
-BaiHuContainer "baihu"
Ubuntu 26.04:
cd /opt/panel-migration
export QL_DATA=/opt/qinglong/data
export BH_DATA=/opt/baihu/data
export BH_CONTAINER=baihu
./run-ql2bh-ubuntu2604.sh
macOS 26:
cd ~/panel-migration
export QL_DATA=~/Containers/qinglong/data
export BH_DATA=~/Containers/baihu/data
export BH_CONTAINER=baihu
./run-ql2bh-macos26.zsh
迁移完成后至少跑这三类检查:
docker logs --tail=80 baihu
docker ps --filter name=baihu
然后登录白虎页面检查:
- 仪表盘里的任务数和变量数。
- 定时任务页里的名称、cron、启停状态。
- 变量机密页里的变量名、标签、启停状态。
- 脚本编辑页里的
qinglong-migrated目录。
人工自动执行方法
如果你自己操作,建议按下面顺序:
- 先在青龙里暂停会产生外部副作用的任务,例如通知、下单、同步、删除类脚本。
- 记录青龙里的环境变量数量、任务数量、脚本目录文件数量。
- 确认白虎是新安装或至少已有备份。
- 把本文附件的四个文件放到同一个迁移目录。
- 按系统选择 PowerShell、Bash 或 Zsh 脚本执行。
- 登录白虎验证数量和页面。
- 随机挑 1 到 2 个低风险任务手动执行测试。
- 确认没问题后,再把青龙任务停用,逐步切到白虎。
不要一迁完就删除青龙数据。至少保留一个完整备份周期。迁移就像转学:新学校点名没问题,不代表旧学校档案当天就该烧掉。
Agent 自动配置方法
如果你让 Agent 操作,建议不要只说“帮我迁移”。可以直接给它这个任务说明:
请从青龙迁移到白虎。白虎已经全新安装完成。
要求:
1. 不允许覆盖白虎已有非 ql2bh 标记的数据。
2. 迁移前必须备份白虎数据库。
3. 迁移时先停白虎容器,迁移后再启动。
4. 迁移脚本只能读取青龙 data 目录,不能删除青龙数据。
5. 迁移脚本需要转换 scripts、envs、crontabs、tags、enabled/disabled 状态。
6. 迁移后用数据库计数、白虎 API、白虎页面三种方式验证。
7. 最终报告迁移数量、备份文件名、遇到的问题和解决办法。
8. 报告里不得出现完整内网地址、真实 token、真实 cookie、真实主机名。
Agent 操作时还要加一句:“如果发现现有容器或目录不是本次实验创建的,不要删除,先报告。” 这句话很重要。因为很多自托管机器上都有长期运行的容器,迁移实验不能顺手清掉别人的服务。
Q&A
Q:能不能只复制青龙 scripts 目录,然后在白虎重新建任务?
可以,这是最保守的方法。脚本少的时候我甚至推荐这样做。本文的迁移器适合任务数量较多、想保留 cron、标签和启停状态的情况。
Q:环境变量值很敏感,脚本会不会泄露?
脚本会把变量值写入白虎数据库,这是迁移本身必须做的事;但不会打印变量值。文章截图里变量值也保持隐藏。生产环境还应该确认白虎数据目录权限和备份文件权限。
Q:青龙订阅能不能一起迁?
本文没有把订阅作为自动迁移范围。原因是白虎支持青龙格式仓库命令解析,但订阅、依赖、分支、白名单、黑名单、自动解析脚本注释等行为需要逐项核对。我的建议是:脚本和任务先迁,订阅用白虎仓库同步能力重新建一遍。
Q:为什么要把任务绑定到所有迁移过来的环境变量?
青龙变量常见用法更偏全局注入。为了让迁移后的任务行为更接近青龙,脚本给任务配置了 $task_all_envs=true,同时写入任务-变量关系,方便白虎页面筛选和排查。后续你可以在白虎里按任务精简变量。
Q:迁移后是不是就能删青龙?
不要急。先让白虎跑一个完整周期,至少覆盖日任务、周任务和关键通知任务。确认日志正常后,再下线青龙。生产迁移最怕“当天看着没问题,周一任务才发现没迁完”。
最后:迁移成功不是命令成功,而是业务成功
这次实验里,命令成功只是第一步。真正让我确认迁移完成的是四个证据:数据库计数对、API 计数对、页面能看到、脚本目录能打开。对自托管系统来说,这比“脚本退出码是 0”更可靠。
青龙迁白虎的关键不是神秘命令,而是顺序:先盘点,先备份,再转换,最后验证。把它当成搬家而不是复制文件,你就会自然地问出正确问题:东西齐不齐,钥匙对不对,标签贴没贴,旧家先别拆。