这是「Docker 好项目」系列的第 12 期。这一期聊一个「信息流焦虑」的解药:RSS 阅读器。如果你还在被各大 App 的信息流喂料、被算法牵着走,Miniflux 能把订阅权重新交回你自己手里。

Miniflux 效果图

痛点开场

每天打开的不是新闻,是十个 App 的开屏广告和推荐流。想看的东西要看平台脸色,不想看的塞满首页,算法还越来越懂怎么让你多划两下。

RSS 是把订阅权还给用户最干净的方式:你订阅谁、看什么、什么时候看,全由你定。但公共 RSS 服务要么停了(Google Reader 的痛很多人还记得),要么开始加广告、限速、卖数据。

Miniflux 就是这个问题的自托管解:一个极简、固执己见(opinionated)的 RSS 阅读器,作者 Frédéric Guillot 一个人维护了很多年,做减法的哲学贯彻到底——不堆功能,只把「读订阅」这件事做到顺手。

能力边界

能干什么 - 抓取 RSS / Atom / JSON Feed,支持大部分站点,自带 content scraper(把只有摘要的源抓成全文) - 全文搜索(底层是 Postgres 的全文检索,不是简单 LIKE) - 多用户、分类、星标、已读历史、规则过滤(按关键词自动标星/标记已读/转发) - 自带 API(Miniflux API + 兼容 Fever API + Google Reader API),Reeder、NetNewsWire、NewsFlash、Flux 等主流客户端直接连 - 30+ 集成:ntfy、Telegram、Discord、Matrix、Slack、webhook,以及 Linkding / Raindrop / Wallabag 等书签/稍后读工具 - 图片走 Media Proxy,不泄露你的 IP 给文章里的图床

不干什么 - 不做社交、不做推荐、不做「猜你喜欢」 - 不存文章到本地文件(数据全在 Postgres 里,没有独立的「附件库」) - 不内置通知中心推送(告警靠集成转发到 ntfy/Telegram 这类外部渠道,详见后文) - 不支持 MySQL / SQLite,只认 PostgreSQL

一句话:Miniflux 只管「把订阅拉下来、整理好、让你读」,其他的一律交给别的工具。

部署前准备

  1. 一台能跑 Docker 的机器(主人这台华为云 ECS 完全够用,Miniflux 本体是 Go 单二进制,内存占用极低,给 256M 都绰绰有余)
  2. 一个 PostgreSQL 数据库(Miniflux 只支持 Postgres,不支持 MySQL/SQLite)。可以单独起一个 Postgres 容器,也可以用 1Panel 的「数据库」模块建一个
  3. 一个域名 + 反向代理(Nginx Proxy Manager / Caddy 都行),后面细说
  4. 镜像:miniflux/miniflux:2.3.3

关于镜像 tag 必须说一句(往期踩过这个坑):Miniflux 的 Docker Hub 只发布完整版本号 tag(2.3.3)和 latest,没有 2.3 或 2 这类次版本/主版本滚动 tag。我实测 miniflux/miniflux:2.3 和 :2 都返回 404,而 :latest 当前的 digest 与 :2.3.3 完全一致。所以这一期钉全量版本 2.3.3,不要图省事写 :latest(哪天升了 2.4 你毫无防备),也不要写 :2 或 :2.3(根本拉不到)。

docker run 一行版

适合先快速验证,正式用建议走下面的 compose:

docker run -d \
  --name miniflux \
  -p 8080:8080 \
  -e "DATABASE_URL=postgres://miniflux:你的密码@postgres主机IP:5432/miniflux?sslmode=disable" \
  -e "RUN_MIGRATIONS=1" \
  -e "CREATE_ADMIN=1" \
  -e "ADMIN_USERNAME=admin" \
  -e "ADMIN_PASSWORD=换一个强密码" \
  miniflux/miniflux:2.3.3

注意:容器里 LISTEN_ADDR 默认被 Dockerfile 设成 0.0.0.0:8080,所以 -p 8080:8080 能直接通。如果你只映射了宿主的 80 端口,就写 -p 80:8080。

RUN_MIGRATIONS=1 第一次必须带——它会自动建表/跑迁移。建完之后其实可以去掉,但留着也无害(幂等)。

docker-compose 推荐版

把 Miniflux 和 Postgres 放一起,最省心:

services:
  miniflux:
    image: miniflux/miniflux:2.3.3
    container_name: miniflux
    restart: always
    ports:
      - "8080:8080"
    depends_on:
      db:
        condition: service_healthy
    environment:
      - DATABASE_URL=postgres://miniflux:secret@db:5432/miniflux?sslmode=disable
      - RUN_MIGRATIONS=1
      - CREATE_ADMIN=1
      - ADMIN_USERNAME=admin
      - ADMIN_PASSWORD=换一个强密码
      - BASE_URL=https://rss.你的域名.com
      # 可选健康检查
      # - HEALTHCHECK 见下方说明
    healthcheck:
      test: ["CMD", "/usr/bin/miniflux", "-healthcheck", "auto"]
      interval: 60s
      timeout: 10s
      retries: 3

  db:
    image: postgres:18
    container_name: miniflux-db
    restart: always
    environment:
      - POSTGRES_USER=miniflux
      - POSTGRES_PASSWORD=secret
      - POSTGRES_DB=miniflux
    volumes:
      - miniflux-db:/var/lib/postgresql
    healthcheck:
      test: ["CMD", "pg_isready", "-U", "miniflux"]
      interval: 10s
      start_period: 30s

volumes:
  miniflux-db:

depends_on ... condition: service_healthy 保证 Postgres 真正能连上之后 Miniflux 才启动,避免首次跑迁移时连库失败。db 用服务名 db 作为主机名(Docker 内部 DNS),所以 DATABASE_URL 里写 db:5432 而不是 IP。

配置项逐项解释

下表是这一期用到的关键环境变量,默认值来自 Miniflux 源码 internal/config/options.go(2.3.3 实测):

变量 默认值 说明 / 为什么这么写
DATABASE_URL user=postgres password=postgres dbname=miniflux2 sslmode=disable Postgres 连接串,支持 libpq 键值对格式或 postgres:// URL 格式。同网络用 db:5432,跨机用 IP。自托管一般 sslmode=disable
RUN_MIGRATIONS 0 必须设 1,否则库里没表,Miniflux 起不来。迁移幂等,可长期保留
CREATE_ADMIN 0 设 1 会在启动时用下面两个变量创建首个管理员。建完账号后可删掉这三项
ADMIN_USERNAME / ADMIN_PASSWORD 空 仅 CREATE_ADMIN=1 时生效。密码别用示例的 test123
BASE_URL http://localhost 非常重要:写成你的公网地址(如 https://rss.xxx.com)。它决定登录后跳转、Cookie 作用域、Fever/API 回调地址。写错会登录后跳回 localhost 或 404
LISTEN_ADDR 配置默认 127.0.0.1:8080,但镜像里被 Dockerfile 改成 0.0.0.0:8080 容器实际监听 0.0.0.0:8080。一般不用动;想换端口改这个 + 映射端口即可
POLLING_FREQUENCY 60(分钟) 整轮所有订阅的抓取间隔。源多就调大,源少想更实时就调小(如 15)
WORKER_POOL_SIZE 16 并发抓取的 worker 数(老版本是 5,2.x 提上来了)。订阅几百个以上可以适当加大
BATCH_SIZE 100 每批处理的订阅数,一般不用动
CLEANUP_FREQUENCY_HOURS 24 清理已读条目历史/会话的间隔
MEDIA_PROXY_MODE http-only 图片代理策略:http-only 只代理 http 图(防 IP 泄露),all 全代理,none 不代理。默认已够隐私,建议保持
FETCHER_ALLOW_PRIVATE_NETWORKS 0 坑点:默认禁止抓取内网地址。如果你要订阅同局域网自建服务的 RSS(比如自己搭的 FreshRSS、Halo 的评论订阅),必须设 1
DISABLE_HSTS 0 Miniflux 检测到 HTTPS 会自动加 HSTS 头。若反代是 Cloudflare「Flexible」模式(TLS 在 CF 就终结,回源是 http),HSTS 会让你被锁在门外,这种情况设 1
TRUSTED_REVERSE_PROXY_NETWORKS 空 走反代时,填反代所在网段(如 172.18.0.0/16 或反代容器 IP),让 Miniflux 正确读取真实客户端 IP,否则日志里全是反代 IP
HTTPS 0 Miniflux 自己不处理 TLS,TLS 交给反代。保持 0

1Panel 面板怎么搞

1Panel 应用商店里没有 Miniflux 官方一键应用,但用 1Panel 跑它很顺:

  • 进入 容器 → 编排,新建一个编排,把上面的 docker-compose 整段粘进去,名称填 miniflux,点部署即可。1Panel 会自动拉起两个容器并建好网络
  • 数据库也可以交给 1Panel:数据库 → PostgreSQL → 创建,建一个 miniflux 库和用户,然后把 DATABASE_URL 指向 1Panel 的 Postgres(注意 1Panel 的 Postgres 端口和容器网络,跨 compose 网络时要用宿主 IP + 映射端口,或把 Miniflux 也放进同一个 1Panel 容器网络)
  • 部署后在 容器 里看 miniflux 容器日志,出现 Starting Miniflux... 且健康检查变绿就 OK

实操提示:华为云这台机器如果 Postgres 也跑在 1Panel 里,建议 Miniflux 和 Postgres 放在同一个 compose / 同一个自定义网络,直接用服务名 db 互通,最省事也最不容易因为端口没开而连不上。

首次访问

  1. 浏览器打开 http://<你的IP>:8080(或在反代配好后打开域名)
  2. 用 ADMIN_USERNAME / ADMIN_PASSWORD 登录(首次建的 admin)
  3. 进去第一件事:设置 → 修改密码,把示例密码换成自己的
  4. 点「添加订阅」,贴一个 RSS 源 URL(比如 https://www.xlee.chat/atom.xml,咱自己博客的订阅源),或导入 OPML(很多阅读器能导出 .opml 文件)
  5. 等几轮抓取(默认 60 分钟一轮)。想马上看到效果,除了在 UI 里点「刷新」,也可以直接在宿主机敲命令强制全量拉取一次:
docker exec miniflux miniflux -refresh-feeds

左边就能读了

Miniflux 的快捷键很全:g u 去未读、j/k 上下、v 在外部打开、r 标已读。用熟了比刷信息流快得多。

最小可用配置

如果你只想先跑起来看效果,下面这份就是最小集(其余全走默认值):

environment:
  - DATABASE_URL=postgres://miniflux:secret@db:5432/miniflux?sslmode=disable
  - RUN_MIGRATIONS=1
  - CREATE_ADMIN=1
  - ADMIN_USERNAME=admin
  - ADMIN_PASSWORD=你的强密码
  # BASE_URL 先不填也能本地用,但一旦上公网务必补上

跑通之后,再按需加 BASE_URL、健康检查、反代相关变量。

告警 / 通知配置

Miniflux 本身没有内置的「系统告警」(不像 Uptime Kuma 那样告诉你「服务挂了」),它的「通知」指的是订阅内容的转发。两套玩法:

1. 内容规则 → 外部渠道(真·通知)

设置里有「规则」(Rules):当文章标题/内容匹配某关键词时,自动「标星 / 标记已读 / 转发到集成」。配合集成里的 ntfy、Telegram、Discord、webhook,就能实现「某某博客更新了 → 推到我手机」。比如给自己的博客源加一条规则:匹配作者名就转发到 ntfy,等于一个轻量更新播报。

2. 服务存活监控(外部保活)

Miniflux 暴露了标准健康检查端点,docker run 里的 healthcheck 就是用它:

/healthcheck
/liveness  /healthz     (存活探针)
/readiness /readyz      (就绪探针)

想监控「Miniflux 还活着」,用 Uptime Kuma(第 1 期讲过的)加一个 HTTP 监控,URL 填 https://rss.你的域名.com/healthz,返回 200 即正常。这样服务挂了你能第一时间收到告警,而不是等想看订阅时发现白屏。

反向代理与 HTTPS

Miniflux 自己不处理 TLS,TLS 交给反代。以 Nginx Proxy Manager(第 2 期)为例:

  • Hostname/IP 填 Miniflux 容器可达的地址:127.0.0.1:8080(若 NPM 和 Miniflux 在同一台机器),或反代容器能解析到的服务名/IP
  • Scheme:http
  • 勾上 SSL(Let's Encrypt 或导入证书)
  • BASE_URL 必须设成公网 HTTPS 地址,二者要对得上

Caddy 用户更省事,一份 Caddyfile:

rss.你的域名.com {
    reverse_proxy 127.0.0.1:8080
}

反代跑在 Cloudflare 后面的注意两点: - 若 CF 用 Flexible(回源 http),Miniflux 的 HSTS 会把你锁门外 → 设 DISABLE_HSTS=1 - 设 TRUSTED_REVERSE_PROXY_NETWORKS 为反代所在网段,否则日志 IP 全错

踩坑清单

  1. tag 别写 :2 / :2.3:这俩在 Docker Hub 上根本不存在(实测 404),只能写全量 2.3.3 或 :latest
  2. BASE_URL 不填 = 登录后跳 localhost:上了公网一定填公网地址,且和反代的 proxy_pass 域名一致
  3. FETCHER_ALLOW_PRIVATE_NETWORKS 默认 0:要抓局域网/同主机其他容器的 RSS,必须设 1,否则连接被拒
  4. Cloudflare Flexible + HSTS 锁门:见上,设 DISABLE_HSTS=1
  5. 只认 Postgres:别拿 MySQL 容器去连,Miniflux 不认;也不需要手动建 hstore 扩展(2.0.27 之后已不需要,2.2.14 还专门跑了移除迁移)
  6. CREATE_ADMIN 只生效一次:账号建好后再重启不会覆盖密码。忘了密码用容器里的 CLI 重设:
docker exec -it miniflux miniflux -reset-password

按提示输入用户名和新密码即可,不需要停服 7. WORKER_POOL_SIZE 老文档写 5:2.x 默认已提到 16,照着老教程调小反而变慢 8. 反向代理后的真实 IP:不配 TRUSTED_REVERSE_PROXY_NETWORKS,所有访问记录都显示反代 IP

备份

Miniflux 没有独立文件库,数据全在 Postgres,所以备份 Postgres 就等于备份了 Miniflux。

# 导出整个 miniflux 库
docker exec miniflux-db pg_dump -U miniflux miniflux > miniflux-$(date +%F).sql

恢复:

docker exec -i miniflux-db psql -U miniflux -d miniflux < miniflux-某日期.sql

另外,订阅源(不含文章)可以用 UI 里的 设置 → 导出 OPML 单独备份,换机器时导入即可。1Panel 的「数据库」模块如果用了它的 Postgres,也能直接在面板里设定时备份,比手写 cron 省心。

小结

适合谁 - 受够了算法推荐、想自己掌控信息流的人 - 已经在跑 Docker / 1Panel、有 Postgres 习惯的自托管玩家 - 需要把「某源更新」推到手机(ntfy/Telegram)做轻量播报

不适合谁 - 想要「社区/发现/推荐」那种社交化阅读体验的(Miniflux 刻意不做) - 不想养一个 Postgres 的极简主义者(那可以看看用 SQLite 的 FreshRSS 单文件版,但 Miniflux 只认 PG) - 订阅量极大又不想调参的(默认配置对几千个源也 OK,但极端的要调 worker / 频率)

把这台华为云 ECS 上的 Miniflux 搭起来,你的信息流就再也不看任何平台的脸色了。

项目地址