记Cloudreve数据库迁移到PostgreSQL
前言
我使用Cloudreve自建网盘,装好后数据库就默认用的是SQLite,随着数据的增加,我打算迁移到PostgreSQL上,以实现更好的性能。
遗憾的是,官方并没有给出不同数据库迁移的工具,那就只能自己尝试迁移了。
导出SQLite
为了保证数据一致性,操作前先把Cloudreve的进程终止了。
首先把SQLite的数据库文件找到,也就是在Cloudreve的data目录下的cloudreve.db文件。
我这里使用Navicat软件的数据传输工具,将其转成PostgreSQL的sql语句,得到data.sql,可以直接用文本编辑。

sql格式调整
事实上,有了data.sql后还不能直接执行导入操作,因为存在着格式不同会报错。
这里需要手动编辑一下,首先有一些字段是boolean类型(都是"is_"开头的字段),但是值为0/1,对应到PostgreSQL应该是FALSE/TRUE。
我是用文本替换实现的(这里注意不要误伤,别把数值类型的0和1改了),其实也可以写个程序更快地处理。
其次是日期的格式问题,在SQLite中日期是存成类似2025-12-21 11:35:34.962063824 +0800 CST m=+91478.155786217的形式,而PostgreSQL中的TIMESTAMPTZ类型不能解析这种格式,会报错,因此要改为2025-12-21 11:35:34.962063824 +0800这种才行。
这里是耗时最久的部分,实际上我参照报错信息调了很久才终于不报错(
安装PostgreSQL
Ubuntu环境下,直接用apt安装一下:
sudo apt install postgresql postgresql-contrib
安装完成后可以这样执行两条命令,进入psql命令行:
sudo su - postgres
psql
之后改一下postgres账号的密码,new_password是新的密码:
ALTER USER postgres WITH PASSWORD 'new_password';
这样创建一下数据库:
CREATE DATABASE cloudreve;
如果要想让服务器外能访问数据库,还需要改一下监听绑定listen_addresses,并添加一下ip白名单,不再赘述。
修改数据库配置
修改Cloudreve的data目录下的conf.ini文件,加入Database的配置,使用我们之前创建的名为cloudreve的数据库:
[Database]
Type = postgres
Port = 端口号
User = postgres
Password = xxx
Host = 127.0.0.1
Name = cloudreve
迁移数据
修改数据库配置后,先启动一次Cloudreve来初始化一下数据库的结构。然后,终止Cloudreve,清空cloudreve数据库中所有数据表的数据。
这样做是为了确保PostgreSQL数据库表结构的正确性,这样我们就可以导入SQLite中的Records数据了,structure相关的sql语句就可以删掉不管了。
id_seq修正
迁移成功后启动Cloudreve,发现文件预览都没什么问题,但是尝试上传文件居然出问题了:

这个报错说明,某一条数据的唯一索引在数据库中已存在,违反了唯一性约束。
按照AI的方法,需要批量修正各表的id_seq为最大id+1,下面的命令可以构造修正sql语句:
SELECT 'SELECT setval(' || quote_literal(quote_ident(s.relname)) ||
', COALESCE(MAX(' || quote_ident(c.attname) || '), 1)) FROM ' ||
quote_ident(t.relname) || ';'
FROM pg_class s
JOIN pg_depend d ON d.objid = s.oid
JOIN pg_class t ON d.refobjid = t.oid
JOIN pg_attribute c ON (d.refobjid = c.attrelid AND d.refobjsubid = c.attnum)
WHERE s.relkind = 'S' AND t.relkind = 'r';
输出了以下内容,都执行一遍:
SELECT setval('direct_links_id_seq', COALESCE(MAX(id), 1)) FROM direct_links;
SELECT setval('entities_id_seq', COALESCE(MAX(id), 1)) FROM entities;
SELECT setval('fs_events_id_seq', COALESCE(MAX(id), 1)) FROM fs_events;
SELECT setval('metadata_id_seq', COALESCE(MAX(id), 1)) FROM metadata;
SELECT setval('oauth_clients_id_seq', COALESCE(MAX(id), 1)) FROM oauth_clients;
SELECT setval('oauth_grants_id_seq', COALESCE(MAX(id), 1)) FROM oauth_grants;
SELECT setval('passkeys_id_seq', COALESCE(MAX(id), 1)) FROM passkeys;
SELECT setval('shares_id_seq', COALESCE(MAX(id), 1)) FROM shares;
SELECT setval('tasks_id_seq', COALESCE(MAX(id), 1)) FROM tasks;
SELECT setval('settings_id_seq', COALESCE(MAX(id), 1)) FROM settings;
SELECT setval('nodes_id_seq', COALESCE(MAX(id), 1)) FROM nodes;
SELECT setval('storage_policies_id_seq', COALESCE(MAX(id), 1)) FROM storage_policies;
SELECT setval('groups_id_seq', COALESCE(MAX(id), 1)) FROM groups;
SELECT setval('users_id_seq', COALESCE(MAX(id), 1)) FROM users;
SELECT setval('dav_accounts_id_seq', COALESCE(MAX(id), 1)) FROM dav_accounts;
SELECT setval('files_id_seq', COALESCE(MAX(id), 1)) FROM files;
这样数据库迁移就宣告完成了。