自建FreshRSS:用Docker轻松部署私有RSS阅读器,夺回信息流控制权
2026/9/24 2:50:05 网站建设 项目流程

说实话,我已经很久没有主动打开过资讯类App了。今年年初我把服务器上的FreshRSS重新部署了一遍,从写docker-compose到服务跑起来只花了几分钟,然后花了一个晚上,把所有还在坚持输出RSS的博客、周刊、项目更新全部收进了同一个阅读列表。每天早上打开FreshRSS,按自己的节奏把未读读完,那种“内容由我决定”的感觉,比刷算法的信息流踏实太多了。这篇文章就把这套FreshRSS部署方案完整拆开来讲,从选型理由、环境准备、一键部署、反向代理,到客户端配置和常见问题排查,尽量做到照着抄就能用。想重新掌控信息流、不想再被推荐算法牵着走的读者,应该能从里面拿到不少有用的东西。

1. 为什么是FreshRSS:自建RSS的方案选型思路

1.1 从算法推荐里抽身,把信息选择权拿回来

先说一个大家都能感受到的问题:现在的资讯类App,本质上不是给你“看内容”的,而是给你“看流量”的。它会根据你的点击、停留时长、购买记录不断调整推送策略,把你留在信息流里越久越好。于是你刷到的内容越来越像,观点越来越重复,标题越来越夸张,真正想看的深度内容反而容易被淹没。这种感觉持续久了,会有一种“看了很多但什么都没记住”的疲惫感。

RSS这种老协议恰好是另一个逻辑:它不追踪你,不猜你喜欢什么,你订阅什么源就推送什么内容,排序完全由你自己决定。没有推荐权重,没有时间线算法,没有乱七八糟的“猜你想看”。很多人觉得RSS已经过时了,但实际上它只是被主流平台藏起来了。大量博客、开源项目、技术社区至今依然在输出RSS/Atom源,反而是最稳定的信息渠道。

选择“自建”而不是注册某个在线RSS服务的理由也很直接:曾经最大的在线RSS阅读器Google Reader说关就关,让无数用户不得不迁移到其他平台,之后不少RSS服务也经历了收费、被收购、数据丢失等波折。自己部署一套FreshRSS,数据文件在自己服务器上,订阅源在自己手里,不用看服务商脸色,也不会被突然通知“服务将于下月停止”。隐私方面也更好,没有中间商读你的阅读习惯,FreshRSS甚至可以完全匿名使用,只要你自己不登录。

1.2 FreshRSS 与 Tiny Tiny RSS、Miniflux 的横向对比

自托管RSS阅读器里,大家提得最多的三个方案是FreshRSS、Tiny Tiny RSS(TTRSS)和Miniflux。我在选型时把三者都跑过一遍,这里直接给一个对比表:

方案技术栈界面风格多用户API兼容资源占用扩展能力
FreshRSSPHP传统Web界面,配置项丰富支持Google Reader API + Fever API中等插件机制完善,扩展多
Tiny Tiny RSSPHP偏Geek,可配合主题插件支持TTRSS自有API中等偏高插件多,但配置略复杂
MinifluxGo极简无JavaScript负担单用户Google Reader API极低扩展少,胜在轻量

我最终选择FreshRSS,核心原因是它的综合成熟度。首先是官方Docker镜像维护得非常好,部署基本不需要手动装PHP环境;其次是API兼容Google Reader协议,这意味着手机端可以直接用Reeder、FeedMe、Read You等热门客户端连接,不用被Web端绑定;第三,它支持多用户,如果你家里有人也想摆脱推荐流,开个子账号就能共用一套服务;第四,FreshRSS的插件系统很丰富,官方扩展仓库里能下载到不少有意义的功能,比如把文章自动推送到通知渠道、按规则过滤垃圾内容等。

Tiny Tiny RSS同样很优秀,但它更适合喜欢深度定制、愿意折腾主题和插件的用户。如果你只想安安静静读个RSS,TTRSS的初始配置成本反而有点高。Miniflux我承认它轻快得惊人,非常适合资源紧张的小机器,但它只支持单用户,而且没有完整的插件体系。对于大多数人来说,FreshRSS是那个“装上就能用、以后想折腾也有空间”的平衡点。

2. 部署前要准备的东西:服务器、Docker与域名

2.1 服务器配置怎么选,域名到底要不要

FreshRSS本身非常轻量,一个订阅源几百到上千条文章,数据量也不会很大。以我实际使用经验看,1核1G内存的云服务器完全够用,如果只是自己一个人用,512MB内存的机器也能跑,只是后台刷新大量订阅源时CPU会短暂飙升。如果你打算同时部署RSSHub、WeWe RSS这类辅助服务,建议直接上2G内存以上,免得后面内存不够再迁移,折腾起来反而浪费时间。

操作系统建议直接用Ubuntu 22.04 LTS或Debian 12,社区的教程和排错经验最多。这里多说一句,我不太推荐在免费的虚拟主机或面板型主机上部署FreshRSS,虽然也有办法跑,但文件权限、计划任务、扩展安装这些环节很容易被平台限制卡住,出了问题排查起来非常费劲。既然目标是“自建”,还是建议找一台能完整掌控的云服务器。

域名这块,我的建议是:如果你只是短期内自己测试,直接用IP加端口访问也行;如果打算长期使用,尤其是要用手机客户端通过API同步阅读,那最好准备一个域名并配置HTTPS。原因有两个,一是FreshRSS的Google Reader API接口涉及账号密码,明文HTTP传输风险太高;二是不少客户端在没有HTTPS的地址下会拒绝连接。域名不用太贵,注册一个普通的就行,反正只需要解析一个二级域名给FreshRSS用,比如freshrss.example.com。

2.2 安装Docker与Docker Compose,把基础环境一次弄好

FreshRSS官方提供了非常成熟的Docker镜像,所有PHP扩展、Apache和运行环境都已经打包好了,所以部署FreshRSS最省心的方式就是用Docker Compose。先把服务器上的Docker环境装好,Ubuntu下推荐直接安装官方源里的docker-ce和docker-compose-plugin:

sudo apt update sudo apt install -y ca-certificates curl gnupg sudo install -m 0755 -d /etc/apt/keyrings curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /etc/apt/keyrings/docker.gpg sudo chmod a+r /etc/apt/keyrings/docker.gpg echo \ "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/ubuntu \ $(. /etc/os-release && echo "$VERSION_CODENAME") stable" | \ sudo tee /etc/apt/sources.list.d/docker.list > /dev/null sudo apt update sudo apt install -y docker-ce docker-ce-cli containerd.io docker-compose-plugin sudo systemctl enable --now docker docker --version docker compose version

装完之后有两个细节容易踩坑:一是老教程里还在用docker-compose(带横杠)命令,那是Compose v1的旧命令,新版Docker默认安装的是docker compose(带空格)插件,用的时候注意区分;二是如果你在云服务商那里拿到的机器有防火墙或安全组规则,记得放行后面要用的端口,不然程序跑起来但你从浏览器访问不通,会误以为是部署失败。

Docker Hub镜像拉取速度在不同地区差异很大,如果拉取FreshRSS镜像特别慢,可以按自己所在地区的情况给Docker配置镜像加速器,修改/etc/docker/daemon.json后重启Docker即可。这一步属于常规网络优化,不在本文展开,遇到问题的人应该都知道怎么做。

3. FreshRSS一键部署实操:完整动手过程

3.1 编写docker-compose.yml,真正的一分钟跑起来

装好Docker之后,在服务器上找一个干净目录,比如/opt/freshrss,创建一个docker-compose.yml。下面是我实际在用的配置,去掉注释后非常简洁:

services: freshrss: image: freshrss/freshrss:latest container_name: freshrss restart: unless-stopped ports: - "8080:80" environment: TZ: Asia/Shanghai CRON_MIN: "*/15 * * * *" FRESHRSS_USER: "admin" FRESHRSS_PASSWORD: "换成你自己的强密码" volumes: - ./data:/var/www/FreshRSS/data - ./extensions:/var/www/FreshRSS/extensions

然后执行:

cd /opt/freshrss docker compose up -d docker compose logs -f freshrss

严格来说,“一分钟”这个说法没有算上镜像下载的时间,首次拉取镜像要看网络情况。但从镜像拉完、执行docker compose up -d,到FreshRSS容器启动完成,一分钟内肯定够。如果一切正常,日志里会出现Apache启动成功的提示,这时打开浏览器访问http://服务器IP:8080就能看到安装界面。

这套配置里有几个要解释的地方。TZ: Asia/Shanghai是时区设置,不设的话服务器上的时间默认是UTC,会导致文章“更新时间”和你的直觉差8个小时。CRON_MIN是FreshRSS容器内部的计划任务表达式,我设的是每15分钟刷新一次订阅源,这个值可以根据你的订阅源数量调整。FRESHRSS_USERFRESHRSS_PASSWORD是预置管理员账号的环境变量,官方镜像的启动脚本会检测到用户未初始化并自动创建管理员,省掉手动填表的步骤。

3.2 Web初始化与首次登录,十分钟内完成基础设置

进入http://服务器IP:8080后,页面会引导你完成FreshRSS的初始化。语言选择简体中文,数据库选择SQLite,然后填入管理员用户名和密码,点击安装就行。这里的SQLite是FreshRSS默认的存储方案,对单用户或几个人的小规模使用完全够用,所有数据就是一个文件,备份时直接把data目录里的数据库文件拷走即可。

如果你预计以后会有几十个用户同时使用,或者想追求更稳定的并发性能,可以考虑在Compose里加一个MariaDB容器,并把FreshRSS的数据库配置指向它。但从实际体验看,个人和小家庭使用场景下SQLite的差别几乎感知不到,我建议先SQLite跑起来,真到规模变大了再迁移。迁移过程FreshRSS后台也有文档说明,不用提前焦虑。

初始化完成之后会自动跳转到登录页,用刚才设置的管理员账号登录。登录后第一件事我建议去“订阅管理”页面,手动添加一两个你常看的博客或技术社区RSS源,测试一下抓取是否正常。确认没问题后,再去“设置”页面检查一下刷新频率、文章保留天数和用户资料。这里有个小经验:文章保留天数不用设成“永久”,我一般设60天,因为大部分RSS文章看完一遍就不会再翻了,长期堆积反而占用数据库空间、拖慢刷新速度。

3.3 加一层Nginx反向代理,把HTTPS证书安排上

为了让手机客户端能安全连接FreshRSS的API接口,我强烈建议在FreshRSS前面加一层反向代理并配置HTTPS。这里有两种主流选择,一种是Nginx配合certbot自动申请Let's Encrypt证书,另一种是直接用Caddy,Caddy会自动申请和续期证书,配置量最少。

如果你已经装了Nginx,先修改FreshRSS的端口映射,让它只监听本地回环地址,避免直接暴露到公网:

ports: - "127.0.0.1:8080:80"

然后新建一个Nginx站点配置:

server { listen 80; server_name freshrss.example.com; location / { proxy_pass http://127.0.0.1:8080; proxy_set_header Host $host; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; } }

执行sudo certbot --nginx -d freshrss.example.com申请证书,certbot会自动改写Nginx配置并开启HTTPS。如果你不想装Nginx,Caddy会更省心,在Caddyfile里写两行就行:

freshrss.example.com { reverse_proxy 127.0.0.1:8080 }

然后systemctl reload caddy,Caddy会自动完成HTTPS证书的申请和续期。这里有一个特别容易踩的坑:只要用了反向代理,就必须在FreshRSS的环境变量里设置TRUSTED_PROXY,否则FreshRSS不知道客户端是通过HTTPS进来的,页面里的资源链接可能还是http,导致样式错乱甚至部分功能异常。以Nginx为例,在docker-compose.yml的environment里加上TRUSTED_PROXY: "127.0.0.1",然后重启容器。Caddy场景下通常也需要这一项,具体值看你的反代部署方式。

4. 让FreshRSS真正好用:订阅、导入与客户端连接

4.1 添加订阅源、批量OPML导入与分类管理

FreshRSS的订阅管理做得比较直观,登录后台后在“订阅管理”页面点击“添加订阅”,把RSS或Atom链接粘进去,选择放到哪个分类下就行。我习惯按“技术博客”“产品思考”“行业资讯”“开源项目”几个分类管理,这样每天打开后可以按分类逐批阅读,不会混在一起显得很乱。

如果你之前用的阅读器支持导出OPML文件,比如Feedly、Inoreader、甚至是旧版Google Reader备份,可以直接在FreshRSS后台导入。OPML是一种标准化的订阅源清单格式,导出的文件记录了所有你已经订阅的源地址和分类结构。导入后FreshRSS会自动重建分类,省去一个个手动添加的麻烦。我当年从旧服务迁移过来,三千多条订阅用OPML一次性搞定,非常省事。

还需要留意的是,FreshRSS的“文章保留天数”和“刷新频率”是两套独立配置。前者控制数据库里最多保留多少天的文章,后者决定后台多久去源站抓一次新内容。如果订阅源数量很多,建议刷新频率不要低于15分钟一次;如果只有几十个源,每小时甚至每天刷新都行,频率越高对源站压力越大,也可能被部分网站限制访问。

4.2 手机端用Reeder、FeedMe或Read You连接FreshRSS

FreshRSS支持Google Reader API和Fever API,这是它比很多同类自托管阅读器都强的地方,因为这意味着移动端生态非常成熟。手机端的体验直接关系到你愿不愿意长期用RSS,如果只能在电脑上读,很难坚持下来。

在FreshRSS后台的“设置”页面找到“认证”或“API”相关选项,启用Google Reader API,记下服务器地址格式:https://freshrss.example.com/api/greader.php。然后用手机端RSS客户端配置账号,以FeedMe为例,选择Google Reader API,填写服务器地址、用户名、密码就行。iOS上很受欢迎的Reeder 5也是类似配置方式,选Google Reader API后填入同一个地址。

我自己现在的组合是:电脑上用FreshRSS网页端,手机上用Read You,配合服务端的“已读同步”功能,在手机上读过的文章,回到电脑上会自动标记为已读,反之亦然。这个多端同步体验和商业阅读器差不多,但它全部跑在自己的服务器上,没有任何第三方介入。需要提醒的是,API连接必须走HTTPS,不然账号密码在公网明文传输,风险太大了。

4.3 过滤规则、标签和通知扩展:把信息流整理成自己的

FreshRSS默认的功能已经够用,但它的魅力还在于可以扩展。后台自带的扩展列表里,我最常用的是“过滤规则”和“Webhooks”相关扩展。过滤规则可以按关键词、来源、标题等维度对文章做自动标记、自动分组或者自动忽略。

举一个实际例子:我订阅了很多技术博客,其中不少会发“促销”“抽奖”“广告”类内容,这些不是我想看的。于是我在过滤规则里加了一条:标题包含“促销”或“广告”的文章自动标记为已读。这样后台刷新后,这些垃圾内容不会出现在未读列表里,阅读效率直接提升。规则可以很灵活,比如临时追某个热点话题,可以建一条规则把所有包含该关键词的文章自动放在一个标签下,方便集中阅读。

如果你用Telegram、Slack、钉钉或类似的通知工具,FreshRSS也有Webhooks扩展,可以设置成“有新的未读文章时推送到某个渠道”。这个适合订阅量不大但需要实时跟踪的场景,比如你订阅了某个项目的Release更新,一旦有新版本发布,立刻推送到自己的通知机器人,比开邮件提醒清爽多了。

5. 进阶玩法:让信息源更全、更自动化

5.1 用WeWe RSS把微信公众号文章接入FreshRSS

很多朋友和我一样,有一批重要的信息源只在微信公众号上更新,但微信生态没有开放RSS,这是自建RSS方案里最让人头疼的缺口之一。好在这几年开源社区已经有了比较成熟的解决方案,比如WeWe RSS,通过本地部署的方式,把你关注到的公众号文章转换生成RSS订阅源,再把这个源添加到FreshRSS里,就能在同一个阅读器里读到公众号内容。

WeWe RSS本身也是一个支持Docker部署的项目,大致思路是部署好容器后,按项目文档的说明完成配置,它会在本地生成一个RSS地址,把这个地址加到FreshRSS就行了。具体命令以项目GitHub仓库的最新说明为准,因为我每次都是按官方文档来装的,版本更新后参数可能有变化。这种工具的使用前提是遵守相应平台的规定,只订阅你自己会正常查看的公开内容,别拿来做爬虫批量采集,也别突破任何平台限制。

5.2 用RSSHub补齐没有RSS源的内容

RSSHub是另一个我非常依赖的开源项目,它做的事情本质上是一个“万物转RSS”的网关:很多网站本身不提供RSS,但RSSHub提供了几十类路由,能把B站、微博、知乎、小红书、YouTube等平台的内容动态生成RSS。部署RSSHub同样用Docker,跑起来后,把对应平台内容的路由地址填进FreshRSS,就可以订阅原本订阅不到的内容。

RSSHub的部署很简单,官方镜像一条命令就能启动。比较推荐的做法是把RSSHub和FreshRSS放在同一台服务器上,这样FreshRSS添加订阅源时可以直接填内网地址,比如http://localhost:1200/bilibili/user/dynamic/某个UID,内网访问稳定也省公网流量。需要注意的是,RSSHub的稳定性很大程度取决于上游平台的反爬策略,有些路由时好时坏,这是正常现象;另外,部分平台的路由需要传入Cookie才能工作,使用时要仔细看官方文档,做好Cookie的保密,也别反复请求给源站造成压力。

5.3 试过用本地模型给文章做自动摘要,体验很有意思

最近不少朋友在折腾本地大模型部署,而FreshRSS其实也能和这个方向结合。大致思路是用FreshRSS的Webhooks扩展,把新文章推送到一个本地脚本,脚本调用部署好的本地模型给文章生成摘要,再把摘要回写到标签里或者推送到通知渠道。

按我自己的实测感受,这件事完全可行,但别期待“零成本”。文字摘要这种任务,普通量级的小模型其实就够用,没必要非得跑一个超大参数量的模型,推理速度和显存开销都要考虑。我目前是在一台带GPU的闲置机器上跑了一个量化过的模型,配合FreshRSS每天固定时段批量处理前一天的文章摘要,效果算是“能用”,但离完美还很远。这个玩法更多是给喜欢折腾的朋友一个方向,如果你已经在搞本地大模型,不妨把FreshRSS接进来试一试。

6. 常见问题与排查技巧实录

6.1 定时刷新不生效,文章迟迟不更新怎么办

这个是我被问得最多的一个问题。症状通常是:FreshRSS安装好了,手动在后台点“刷新”能抓到文章,但放着不管,过一晚上也没有新内容进来。

绝大多数原因是CRON_MIN环境变量没有设置,或者容器内的定时任务没有启动。FreshRSS镜像内部其实集成了cron,但需要你通过环境变量告诉它什么时间执行。检查方法很简单,在服务器上执行:

docker compose exec freshrss crontab -l

如果输出为空,说明容器内没有计划任务,需要检查你的docker-compose.yml里是否设置了CRON_MIN。设置后记得重新创建容器:

docker compose up -d --force-recreate freshrss

如果仍然不生效,可以手动验证更新脚本是否正常:

docker compose exec freshrss php cli/update.php --cron

这条命令会强制FreshRSS执行一次订阅源抓取。如果手动执行没问题,那问题基本就锁定在cron配置上;如果手动执行也报错,再去查数据目录权限和日志输出。

6.2 用了反向代理后页面样式丢失、一直跳http

这个问题在Nginx反代场景下非常典型。症状是:通过域名访问FreshRSS,页面能打开,但CSS、图片加载失败,页面光秃秃的;或者登录后跳转回http地址,导致浏览器报警。

原因在于FreshRSS收到请求时没有正确识别出“客户端是通过HTTPS访问的”,它以为自己还在普通的http环境里,于是生成资源链接时全用了http协议。解决办法就两条:

  1. 确保Nginx配置里设置了proxy_set_header X-Forwarded-Proto $scheme;
  2. docker-compose.yml的environment里加上TRUSTED_PROXY,并指向反向代理服务器的IP(也就是运行Nginx的那台机器,本机场景下就是127.0.0.1

配置完成后重启FreshRSS容器,再强制刷新浏览器页面,样式一般就恢复正常了。Caddy用户虽然没有前面那个header问题,但TRUSTED_PROXY该设还是要设,不然FreshRSS记录的客户端IP可能全是代理的地址,影响日志分析和部分扩展的判断。

6.3 订阅源抓取失败,显示超时或被拒绝

订阅源抓取失败的原因五花八门,但大部分集中在几个方向。我习惯先拿浏览器直接打开这个RSS地址,如果浏览器也打不开,那说明源站本身就不健康;如果浏览器能打开但FreshRSS显示超时,大概率是服务器到源站的网络链路有问题,或者是源站对自动化请求有拦截。

不少网站会对抓取请求的User-Agent做过滤,FreshRSS默认的UA可能被部分源站拒绝。解决办法是在FreshRSS后台的“设置”里找到抓取参数,把User-Agent改成常见浏览器的UA,比如Mozilla/5.0开头的一段完整UA。另外,有些站点的RSS只提供http版本,服务器如果强制走HTTPS反而握手失败,这种情况可以手动把订阅源地址改成http试一次,虽然不推荐长期用明文协议,但部分老旧源确实是这个情况。

下面这张速查表是我平时排查问题用的,直接贴出来给大家参考:

现象可能原因解决办法
安装页无法访问端口未映射、安全组未放行检查docker ps端口,放行防火墙规则
后台能打开但文章不更新未设置CRON_MIN,或cron未生效设置环境变量,重建容器,手动执行update.php验证
反代后样式错乱、跳http缺少X-Forwarded-ProtoTRUSTED_PROXY补上代理头和TRUSTED_PROXY,重启容器
某订阅源永远抓取失败UA被拦截、http/https不匹配、源地址失效换成浏览器UA,尝试http,浏览器打开确认
手机客户端无法连接APIAPI未开启、未走HTTPS、地址错误开启Google Reader API,用HTTPS域名,检查API路径
忘记管理员密码用容器内CLI创建新用户或重置密码

6.4 数据目录越来越大,资源占用持续走高

FreshRSS用久了之后,data目录下的SQLite数据库文件会越来越大,尤其是你同时订阅了几百个更新频繁的源,文章每天都在大量堆积。服务器内存和CPU占用升高,多半也是后台刷新任务在扫描海量历史文章所致。

这个问题的解决办法有两个层面。第一是控制保留天数,在FreshRSS后台把文章保留期限从“永久”改成30天或60天,FreshRSS的清理机制会定期删除过期文章。第二是调整刷新频率,把全局CRON_MIN从每5分钟一次改到每30分钟或每小时一次,对大部分人的阅读习惯来说完全够用。我自己的经历是,订阅源从一百多涨到五百多之后,把刷新频率从15分钟改到30分钟,CPU占用明显下来了,文章更新延迟也并没有感知到多大差异。

还有一个小经验是定期做SQLite的完整性检查和备份。备份最简单,直接打包整个data目录就行:

tar czf freshrss-backup-$(date +%F).tar.gz /opt/freshrss/data

恢复的时候把data目录解压回去,然后重建容器即可。对普通用户来说,这套“文件级备份”已经足够安全了。


这套自建FreshRSS的方案,我自己从第一次安装到现在已经稳定跑了一年多。严格来说,RSS解决不了“内容过剩”的问题,它只是把“谁来决定你读什么”这个权利还给了你。我最深的体会是,自建的真正收益不是省下几十块订阅费,而是每一条过滤规则、每一个订阅源、每一台连接设备都由自己掌控。如果你也腻了刷不完的算法流,不妨就从今天这个部署开始。先让它跑起来,再花点时间慢慢把信息源整理成自己喜欢的样子,这件事值得做。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询