Miniflux 部署与迁移:保留 RSS 订阅、已读状态和收藏
固定 Miniflux 2.3.3 与 PostgreSQL 17.11,建立私有入口和业务角色,核对应用重建、数据库恢复与 OPML 导入的差别。
换 VPS 时,导入 OPML 可以找回订阅地址,但以前读过什么、收藏过什么,不会跟着订阅列表回来。Miniflux 把账号、订阅和阅读记录存在 PostgreSQL 里;要保留这些状态,需要恢复数据库,并让新应用连接它。
下面固定 Miniflux 2.3.3 与 PostgreSQL 17.11,先建立一个只有自己能访问的实例,再做数据库备份与恢复。已有 Linux VPS、Docker Engine、Compose 插件和 SSH 账户即可开始。安装前确认示例端口、项目名和数据卷没有被其他服务使用;这套新安装步骤不能直接套到已有数据库上。
固定应用与数据库,先留一个私有入口
在自己的服务器建立项目目录,生成随机凭证。示例密码使用十六进制字符,避免在数据库连接 URL 中另做特殊字符转义;.env 含密码,不提交到公开仓库。
mkdir -p "$HOME/miniflux"
cd "$HOME/miniflux"
umask 077
printf 'BOOTSTRAP_PASSWORD=%s\nDB_PASSWORD=%s\nADMIN_PASSWORD=%s\nBASE_URL=http://127.0.0.1:18082\n' \
"$(openssl rand -hex 24)" "$(openssl rand -hex 24)" \
"$(openssl rand -hex 24)" > .env
chmod 600 .env
保存为 compose.yaml:
name: miniflux-reader
services:
db:
image: postgres:17-alpine@sha256:b0f9560a2de083e2cc7382e75f808c7381a32852a7ec49117deedb300e552b24
restart: unless-stopped
environment:
POSTGRES_USER: bootstrap
POSTGRES_DB: postgres
POSTGRES_PASSWORD: ${BOOTSTRAP_PASSWORD:?set BOOTSTRAP_PASSWORD}
volumes:
- db-data:/var/lib/postgresql/data
healthcheck:
test: ["CMD", "pg_isready", "-U", "bootstrap", "-d", "postgres"]
interval: 5s
timeout: 5s
retries: 12
miniflux:
image: miniflux/miniflux:2.3.3@sha256:49d7b60987616387c306a8023087b31f2c9b7b21288b523026cb04058e8b6dbb
restart: unless-stopped
ports:
- "127.0.0.1:8082:8080"
environment:
DATABASE_URL: postgres://miniflux:${DB_PASSWORD:?set DB_PASSWORD}@db:5432/miniflux?sslmode=disable
BASE_URL: ${BASE_URL:?set BASE_URL}
RUN_MIGRATIONS: "1"
CREATE_ADMIN: "1"
ADMIN_USERNAME: admin
ADMIN_PASSWORD: ${ADMIN_PASSWORD:?set ADMIN_PASSWORD}
depends_on:
db:
condition: service_healthy
volumes:
db-data:
两个摘要都是多平台镜像索引,运行时由 Docker 选择对应架构。本次隔离验证使用 Docker Desktop 的 ARM64 镜像;没有把 ARM64 子镜像的摘要直接给 AMD64 服务器使用。PostgreSQL 17 的数据路径是 /var/lib/postgresql/data,不能照 PostgreSQL 18 的示例改挂载后直接启动旧数据。
数据库没有发布宿主机端口,应用只发布回环入口。sslmode=disable 用于本例同一 Docker 网络里的数据库连接,不代表浏览器登录可以走公网明文。
建立应用专用角色,再初始化账号
先只启动数据库,等 docker compose ps 中的 db 健康后,再建业务角色和空数据库:
docker compose config --quiet
docker compose up -d db
docker compose ps
set -a
. ./.env
set +a
docker compose exec -T db psql -U bootstrap -d postgres \
-v ON_ERROR_STOP=1 <<SQL
CREATE ROLE miniflux LOGIN PASSWORD '${DB_PASSWORD}'
NOSUPERUSER NOCREATEDB NOCREATEROLE;
CREATE DATABASE miniflux OWNER miniflux;
SQL
docker compose up -d miniflux
docker compose logs --tail=100 miniflux
这段 SQL 只用于上述全新的空实例。角色或数据库已经存在时,先核对原部署,别删库重跑。bootstrap 是这个独立 PostgreSQL 实例的管理账户,应用连接使用 miniflux,不把管理账户密码交给应用。
在自己的电脑打开 SSH 转发,并保持该终端运行:
ssh -N -L 127.0.0.1:18082:127.0.0.1:8082 user@YOUR_VPS_IP
访问 http://127.0.0.1:18082,用 admin 与自己 .env 中的 ADMIN_PASSWORD 登录。这里的 HTTP 入口在电脑回环地址,电脑与 VPS 之间由 SSH 传输。先添加一个自己有权访问的公开 RSS 地址,检查分类与文章,再把一篇文章标成已读、收藏,留作恢复验收样本。首页返回 200 还不足以证明这些操作成功。
首次初始化成功后,把 Compose 中 CREATE_ADMIN 改为 "0",删除 ADMIN_USERNAME、ADMIN_PASSWORD,将 RUN_MIGRATIONS 改为 "0",再执行 docker compose up -d miniflux 重建应用。管理员已存在于数据库中;检查原账号仍能登录、订阅和阅读状态还在。以后升级需要迁移时,再按对应版本说明安排,不能一直靠初始化参数重建账号。
用域名访问时,先接 HTTPS 再换地址
需要手机直接访问时,为自己的域名配置 DNS 和 HTTPS。宿主机 Caddy 可以代理到上述回环端口:
rss.example.com {
reverse_proxy 127.0.0.1:8082
}
把 .env 的 BASE_URL 改为真实的 https://rss.example.com,重建 Miniflux,然后从手机和未登录窗口分别检查入口、证书与认证。Caddy 在容器里时,它的 127.0.0.1 指向该容器,应另按共同网络设置上游。具体证书和端口前提见宿主机 Caddy 反向代理教程。
本次数据恢复实验用的是受信任的本机自签证书,验证了认证访问和不信任证书时的拒绝;没有验证公网 ACME 签发、你的 DNS 或 Linux 防火墙。不要关闭浏览器或客户端的证书校验来绕过证书问题。
OPML 留订阅清单,数据库留阅读状态
从 Miniflux 导出 OPML,另存一份订阅地址与分类清单,换阅读器时可以使用。完整迁移还需要数据库备份,以及 Compose、实际镜像摘要、部署配置和受保护的凭证。
这次隔离实验使用两篇合成 RSS 文章,把其中一篇标为已读并收藏。应用容器重建后状态不变;将 PostgreSQL 自定义格式备份恢复到另一套空数据库后,账号、分类、订阅、两篇文章及已读、收藏状态也一致。
随后让合成来源只保留较新的一篇,把 OPML 导入另一份全新数据库。导入建立了订阅和分类,人工刷新后只取得来源当前提供的一篇,状态为未读、未收藏;数据库恢复实例仍保留两篇及原阅读状态。这里比较的是一份合成样本,不能保证任意来源都会长期保留旧文章。
实验为隔离状态变化关闭了定时调度,并允许抓取专属 Docker 网络里的合成 RSS。上面的普通部署配置没有这两个实验开关:保留正常调度和默认的私网抓取限制,不为让某个订阅成功而允许抓取任意内网地址。OPML 导入后的抓取时间也与实际调度有关,列表出现订阅不等于文章已经抓取完成。
暂停应用,导出可恢复的数据库
下面先暂停应用写入,导出成功后再恢复服务。数据库容器保持运行,失败文件保留为 .partial,不能当作成功备份。
cd "$HOME/miniflux"
umask 077
mkdir -p backups
backup_file="$PWD/backups/miniflux-$(date -u +%Y%m%dT%H%M%SZ).dump"
docker compose stop miniflux
if docker compose exec -T db pg_dump -U bootstrap -d miniflux \
-Fc --no-owner > "$backup_file.partial"; then
mv "$backup_file.partial" "$backup_file"
else
docker compose start miniflux
exit 1
fi
docker compose start miniflux
docker compose exec -T db pg_restore --list < "$backup_file"
pg_restore --list 能检查备份格式和对象目录,实际恢复仍要在独立实例完成。这个数据库导出不包含整个 PostgreSQL 集群的角色,也不自动保存 .env、代理配置或异地副本。数据库内还有账号认证资料和可能含私有令牌的订阅地址,备份应放在受保护的独立存储,并另安排加密与上传。不要把唯一副本留在同一块 VPS 磁盘上。
在独立项目里恢复,不覆盖原数据库
保留原实例和原卷。复制 compose.yaml 为 restore.yaml,核对并修改应用服务:宿主机端口改为 127.0.0.1:8083:8080,BASE_URL 改为 http://127.0.0.1:18083,RUN_MIGRATIONS 与 CREATE_ADMIN 均为 "0",移除管理员初始化字段。数据库镜像与逻辑卷名仍保持上面的固定设置,但通过不同项目名建立独立卷;不要把 db-data 改成指向原卷的外部卷或固定全局名称。
同机演练可以在原项目目录放置 restore.yaml;换服务器时,先把数据库备份、Compose 和 .env 安全复制到新机器的私有项目目录,核对副本和读取权限。不要把原终端的变量当成新 SSH 会话里仍然存在的值。
下面在项目目录中明确指定要恢复的备份路径。把示例绝对路径换成实际文件,检查它非空且可读,再启动恢复项目的数据库、建立同名业务角色与空库:
cd "$HOME/miniflux"
backup_file='/absolute/private/path/miniflux-YYYYMMDDTHHMMSSZ.dump'
test -r "$backup_file" && test -s "$backup_file" || exit 1
set -a
. ./.env
set +a
docker compose -p miniflux-restore -f restore.yaml up -d db
docker compose -p miniflux-restore -f restore.yaml ps
# 等恢复项目 db 健康后执行;这是另一套全新的数据库
docker compose -p miniflux-restore -f restore.yaml exec -T db \
psql -U bootstrap -d postgres -v ON_ERROR_STOP=1 <<SQL
CREATE ROLE miniflux LOGIN PASSWORD '${DB_PASSWORD}'
NOSUPERUSER NOCREATEDB NOCREATEROLE;
CREATE DATABASE miniflux OWNER miniflux;
SQL
if ! docker compose -p miniflux-restore -f restore.yaml exec -T db \
pg_restore -U bootstrap -d miniflux --role=miniflux \
--no-owner --no-acl --exit-on-error < "$backup_file"; then
echo '恢复失败:保留原实例与失败记录,不启动恢复应用' >&2
exit 1
fi
docker compose -p miniflux-restore -f restore.yaml up -d miniflux
--role=miniflux 让恢复对象归业务角色;角色必须先存在,不能只把一份 pg_dump 当作整套账号与权限安装。应用此时不运行迁移,也不从环境创建新账号。用另一个 SSH 转发访问恢复副本:
ssh -N -L 127.0.0.1:18083:127.0.0.1:8083 user@YOUR_VPS_IP
用原 Miniflux 账号密码登录,核对分类、订阅地址、早期文章、已读与收藏,并打开几篇实际内容。测试副本不要接正式通知或第三方集成;验证后安排自己的切换步骤,不能让副本和原实例同时向外发消息。恢复实例失败时先看日志、数据库连接、卷和权限,保留原实例,不用 docker compose down -v 删除数据来“修复”。
保留周期和升级要另作安排
固定版本 2.3.3 默认清理周期为已读 60 天、未读 180 天,清理任务默认每 24 小时运行。对应源码按条目的 created_at 判断时间,并限定阅读状态、未收藏、无分享码;符合条件的记录会删除并留下去重墓碑。不能把“60 天”理解成每次标记已读后重新计时。
CLEANUP_ARCHIVE_READ_DAYS、CLEANUP_ARCHIVE_UNREAD_DAYS 可以按需要调整,负值会跳过对应清理。延长保留时间要一起观察数据库和备份增长;收藏也不能替代独立备份。本次短时恢复实验关闭了调度,没有验证长期清理行为,更没有证明能永久保留所有阅读历史。
更新 Miniflux 前,按官方升级说明检查变更、保存旧镜像与对应数据,先在副本验证迁移。PostgreSQL 跨大版本需要逻辑导出恢复或专门升级流程,不能把 17 改成 18 后沿用原数据卷;新版迁移过的数据库,也不能只换回旧应用镜像就当作回退。本次验证的是新安装、同版本重建和同版本数据库恢复,没有测试两个应用版本之间的升级。
为阅读器选机器,先算数据与维护成本
已有机器有余量时,可以继续使用,给 PostgreSQL、导出包和独立备份留空间。订阅数、文章体积、抓取频率、保留周期及其他同机容器都会影响资源,不能按空白实例启动时的占用承诺能订阅多少来源。先用自己的订阅副本观察内存、磁盘增长和恢复所需空间,再决定规格。
需要另买 Linux 实例时,可比较 雨云 RCS 的地区与配置 与 Evoxt 的 Linux 实例。核对续费、流量、磁盘与独立备份费用,从自己的常用网络试登录,也从服务器试抓取实际公开来源;阅读器能打开但来源抓取受限,是不同的选址问题。先用短周期验证认证、定时抓取和恢复,再延长账期,不为一份订阅列表购买尚未验证用途的高规格。




