我先把话说在前面:RSS 这个古董协议,到现在依然是我获取信息的主干道。尤其 Google Reader 死掉之后,自托管 RSS 阅读器这件事反而成了刚需。Miniflux 是我测下来最适合个人和小团队长期跑的方案,Go 写的单二进制,部署不折腾,界面简洁到像一块白豆腐,但背后该有的功能一个不少。这篇文章不整虚的,就讲怎么用 Docker Compose 从零搭一套带高可用意味的 Miniflux,直接把思路、配置、坑和运维检查清单都给你。
1. 为什么选择 Docker Compose 承载 Miniflux,以及"高可用"的真实含义
在动手写 Compose 文件之前,得先把两个问题想明白:为什么是 Docker Compose,以及你自己对"高可用"的预期到底是什么。
1.1 从单机二进制到容器化的演进逻辑
Miniflux 官方其实给了很清爽的安装方式——一个 40MB 左右的二进制文件,配一个 PostgreSQL 就能跑。单机部署确实够轻,问题在于升级、备份恢复、迁移主机这几个环节,手动操作越多,事故概率越大。用 Docker Compose 本质上是把"进程管理、网络配置、依赖编排、环境变量注入"四件事用声明式文件一次性确定下来。同一套 Compose 文件在家里的 NAS 和云主机上可以保持完全一致的行为,这个一致性是运维爽感的主要来源。
Docker Compose 另外一个被忽略的价值是"依赖生命周期管理"。Miniflux 必须依赖 PostgreSQL,数据库如果起不来,应用容器就算启动了也会在健康检查里失败。Compose 的 depends_on 加上健康状态等待,能确保启动顺序可控。这比 systemd 守护三个独立服务要直观得多。
1.2 高可用是个分层问题
实话说,个人或者小型团队跑 RSS 阅读器,99% 的情况下用不上企业级的自动故障转移。但高可用也不是遥不可及的概念,落到这个场景,它至少分成三个层次:
- 数据不丢:PostgreSQL 数据有定期备份,且备份可以被验证恢复。
- 服务能重建:应用容器崩溃后能自动重启,而不是等人工介入。
- 访问可靠:数据库出现单点故障时,能切换到备用节点继续服务,或者至少保证从备份恢复的时间可控。
本文会覆盖第一层和第二层,第三层给出一种轻量的 PostgreSQL 主从切换方案,不是 Patroni 那种重量级全家桶,而是适合 1-2 台主机的务实做法。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 架构设计:从单容器到双节点 PostgreSQL 主从
既然要谈高可用,先别急着写代码。把架构图画在脑子里,比直接改 Compose 文件重要得多。
2.1 目标拓扑说明
整套系统由三类角色组成:
- Nginx 作为唯一入口,负责 TLS 终结、HTTP 到 HTTPS 跳转、反向代理到 Miniflux 容器。
- Miniflux 应用容器,可以水平扩展为多个实例,所有实例共享同一个 PostgreSQL。
- PostgreSQL 数据层,采用主从异步流复制,主库故障时手动/半自动触发切换。
这套拓扑的巧妙之处在于:Miniflux 本身是无状态应用,用户会话 token 和订阅数据全部存在数据库里,所以应用层可以随意扩缩容。数据库层的"主从"解决的是单点故障,而不是读写性能——RSS 阅读器的写入频率不高,一台从库用于只读备用已经很充裕。
2.2 持久化与网络规划
容器重启可以丢内存,但绝不能丢数据。所以数据卷要单独命名,绑定宿主机的固定目录,避免 Docker 自动创建的匿名卷在容器重建时被清理。网络部分我建议使用 Compose 内部网络,限制容器间互相暴露端口,只有 Nginx 需要对外监听 80/443。
端口分配上,内部网络不暴露 PostgreSQL 5432 给宿主机,只让 Miniflux 容器访问。Nginx 对外监听,Miniflux 和 PostgreSQL 一律不发布到宿主机,这在云服务器上能少很多扫描攻击的麻烦。
3. Compose 文件逐段拆解:每行配置都有它的道理
直接看生产可用的 docker-compose.yml,然后我逐段解释关键点。注意这里面删减了很多花哨的配置,只保留真正影响稳定性的部分。
yaml复制version: "3.8"
x-app-base: &app-base
restart: unless-stopped
networks:
- miniflux-net
logging:
driver: json-file
options:
max-size: "10m"
max-file: "3"
environment:
TZ: "Asia/Shanghai"
services:
db-primary:
image: postgres:15-alpine
container_name: miniflux-db-primary
<<: *app-base
volumes:
- pg-primary-data:/var/lib/postgresql/data
- ./backup:/backup
environment:
POSTGRES_DB: miniflux
POSTGRES_USER: miniflux
POSTGRES_PASSWORD: ${DB_PASSWORD}
healthcheck:
test: ["CMD-SHELL", "pg_isready -U miniflux -d miniflux"]
interval: 10s
timeout: 5s
retries: 5
start_period: 10s
command: |
postgres
-c wal_level=replica
-c max_wal_senders=10
-c wal_keep_size=128
-c hot_standby=on
-c archive_mode=on
-c archive_command=/bin/sh -c 'cp %p /backup/wal/%f'
app:
image: min
