Miniflux Docker Compose 部署实战:从基础单机到 Caddy/Traefik 自动 HTTPS
Miniflux Docker Compose 部署实战:从基础单机到 Caddy/Traefik 自动 HTTPS
本指南以仓库 contrib/docker-compose 目录下的三份编排文件为核心,系统讲解如何用 Docker Compose 部署开源极简 RSS 阅读器 Miniflux。你将掌握基础版 basic.yml 的完整启动流程、DATABASE_URL / RUN_MIGRATIONS / CREATE_ADMIN 等关键环境变量的真实含义,以及如何通过 Caddy 或 Traefik 反向代理为 Miniflux 自动签发 HTTPS 证书;同时结合源码级证据,理解迁移、管理员初始化与健康检查背后的运行原理。
目录结构与三套方案概览
contrib/docker-compose 目录下提供了四份可直接使用的文件,其中三份 Compose 编排分别对应三种由浅入深的部署形态:
- basic.yml:最简部署,Miniflux 直接通过宿主机端口对外提供服务,适用于内网或快速试用;
- caddy.yml:以 Caddy 2 作为反向代理,配合 Caddyfile 自动申请与续期 HTTPS 证书;
- traefik.yml:以 Traefik 2.3 作为反向代理,通过 Docker Provider 自动发现服务并签发证书。
三种方案共用同一套 PostgreSQL 数据库服务,启动命令仅需替换 -f 指定的文件名:
docker compose -f basic.yml up -d
docker compose -f caddy.yml up -d
docker compose -f traefik.yml up -d
前置条件:镜像与默认配置
所有 Compose 文件中的 Miniflux 镜像均使用变量 MINIFLUX_IMAGE,默认值为 miniflux/miniflux:latest:
image: ${MINIFLUX_IMAGE:-miniflux/miniflux:latest}
也就是说,无需修改文件即可直接使用官方镜像;如需固定版本或使用私有镜像仓库,可通过环境变量覆盖:
MINIFLUX_IMAGE=miniflux/miniflux:2.2.3 docker compose -f basic.yml up -d
镜像的构建方式同样沉淀在仓库中:packaging/docker/alpine/Dockerfile 使用多阶段构建,运行时基于 Alpine,内置 EXPOSE 8080 并以非 root 用户(UID 65534)运行;packaging/docker/distroless/Dockerfile 则提供 Distroless 变体,两者都在运行时通过 ENV LISTEN_ADDR=0.0.0.0:8080 绑定全部网卡的 8080 端口——这正是 Compose 文件中用 80:8080 或 expose 8080 映射的依据。若需自行构建镜像,Makefile 提供了 docker-image、docker-image-distroless 与 docker-images(多架构推送)目标。
方案一:basic.yml 基础部署
basic.yml 是官方提供的标准入门编排,完整内容如下:
services:
miniflux:
image: ${MINIFLUX_IMAGE:-miniflux/miniflux:latest}
container_name: miniflux
restart: always
ports:
- "80:8080"
depends_on:
db:
condition: service_healthy
environment:
- DATABASE_URL=postgres://miniflux:secret@db/miniflux?sslmode=disable
- RUN_MIGRATIONS=1
- CREATE_ADMIN=1
- ADMIN_USERNAME=admin
- ADMIN_PASSWORD=test123
- DEBUG=1
# Optional health check:
# healthcheck:
# test: ["CMD", "/usr/bin/miniflux", "-healthcheck", "auto"]
db:
image: postgres:latest
container_name: postgres
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,只有数据库通过健康检查后 Miniflux 才会启动,从根本上避免了“应用先于数据库就绪而崩溃退出”的竞态问题。数据库的健康检查由 pg_isready -U miniflux 承担,每 10 秒执行一次,并给予 30 秒启动宽限期(start_period)。
数据持久化方面,PostgreSQL 数据目录 /var/lib/postgresql 挂载到名为 miniflux-db 的命名卷上,容器重建后数据依然保留。container_name 被显式固定为 miniflux / postgres,便于日志查询(docker logs postgres)与容器间直接引用服务名。
环境变量逐项解析
| 环境变量 | 值 | 作用 |
|---|---|---|
DATABASE_URL |
postgres://miniflux:secret@db/miniflux?sslmode=disable |
PostgreSQL 连接串:用户 miniflux、密码 secret、主机 db(即 Compose 网络中的 db 服务名)、库名 miniflux,禁用 SSL 模式 |
RUN_MIGRATIONS |
1 |
容器启动时自动执行数据库结构迁移 |
CREATE_ADMIN |
1 |
自动创建初始管理员账号 |
ADMIN_USERNAME |
admin |
初始管理员用户名 |
ADMIN_PASSWORD |
test123 |
初始管理员密码 |
DEBUG |
1 |
输出调试日志,便于首次排错 |
关键变量的源码级原理
迁移机制。 internal/cli/cli.go 在启动守护进程前会检查 config.Opts.RunMigrations(),为真则调用 database.Migrate(db) 依次执行 internal/database/migrations.go 中按顺序追加的迁移函数,随后还会通过 database.IsSchemaUpToDate(db) 校验结构版本。如果关闭 RUN_MIGRATIONS 而数据库结构过旧,进程将直接报错退出。该选项默认关闭(见 internal/config/options.go 中 parsedBoolValue: false),因此 Compose 文件中显式设为 1 是首次部署的必要条件。
管理员创建。 CREATE_ADMIN=1 时,cli.go 会调用 createAdminUserFromEnvironmentVariables,从 ADMIN_USERNAME / ADMIN_PASSWORD 读取凭据创建管理员。internal/cli/create_admin.go 显示创建前会先检查用户名是否已存在,存在则直接跳过(并记录日志),因此该变量在容器重复重启时是幂等安全的。创建请求会经过 internal/validator 的校验并标记 IsAdmin: true。
配置解析。 internal/config/parser.go 将所有环境变量按 KEY=VALUE 格式逐行解析:未知键会被忽略(避免被无关环境变量干扰),布尔值支持 1/0/yes/no/true/false/on/off 等写法;postParsing 阶段还会校验 BASE_URL 必须为 http 或 https 协议。这正是 Compose 中各种配置项能以字符串形式传入并被正确转换的底层依据。
可选:为 Miniflux 容器添加健康检查
basic.yml 中保留了一段注释掉的健康检查配置,可自行启用:
healthcheck:
test: ["CMD", "/usr/bin/miniflux", "-healthcheck", "auto"]
interval: 30s
timeout: 5s
start_period: 10s
-healthcheck auto 会依据运行配置自动拼接探活地址。internal/cli/health_check.go 的实现表明,auto 模式将请求发送到 http://<LISTEN_ADDR>/<BASE_PATH>/healthcheck,随后通过 HTTP 200 状态码判定健康;而 internal/http/server/healthcheck.go 中的就绪探针会真正 Ping 数据库,数据库不可达时返回 503。此外 internal/http/server/routes.go 还在根路径提供了 /liveness、/healthz、/readiness、/readyz 等标准端点,可被外部探针直接使用。
方案二:caddy.yml + Caddyfile 自动 HTTPS
caddy.yml 在基础方案之上增加了 Caddy 2 反向代理服务,将公网 80/443 流量转发给 Miniflux:
services:
caddy:
image: caddy:2
container_name: caddy
depends_on:
- miniflux
ports:
- "80:80"
- "443:443"
volumes:
- $PWD/Caddyfile:/etc/caddy/Caddyfile
- caddy_data:/data
- caddy_config:/config
miniflux:
image: ${MINIFLUX_IMAGE:-miniflux/miniflux:latest}
container_name: miniflux
depends_on:
db:
condition: service_healthy
environment:
- DATABASE_URL=postgres://miniflux:secret@db/miniflux?sslmode=disable
- RUN_MIGRATIONS=1
- CREATE_ADMIN=1
- ADMIN_USERNAME=admin
- ADMIN_PASSWORD=test123
- BASE_URL=https://miniflux.example.org
db:
image: postgres:latest
container_name: postgres
environment:
- POSTGRES_USER=miniflux
- POSTGRES_PASSWORD=secret
volumes:
- miniflux-db:/var/lib/postgresql
healthcheck:
test: ["CMD", "pg_isready", "-U", "miniflux"]
interval: 10s
start_period: 30s
volumes:
miniflux-db:
caddy_data:
caddy_config:
配套的 Caddyfile 极为简洁,仅两行:
miniflux.example.org
reverse_proxy miniflux:8080
Caddy 会自动为 miniflux.example.org 申请 Let's Encrypt 证书并透明续期,无需手动管理证书文件。注意这里 Miniflux 服务不再映射宿主机端口,仅暴露给 Compose 内部网络,由 Caddy 通过服务名 miniflux 与端口 8080 访问。caddy_data 与 caddy_config 两个卷用于持久化证书与 Caddy 配置,避免每次重建容器都重新签发证书。
必须同步设置 BASE_URL
与 basic.yml 相比,此方案为 Miniflux 增加了关键配置:
- BASE_URL=https://miniflux.example.org
Miniflux 需要知道自己对外暴露的公网地址,才能生成正确的绝对链接(如 feed 订阅地址、OPML 导出地址等)。该值的默认值是 http://localhost(internal/config/options.go),未配置时会生成无法被外部订阅的链接。请务必将 miniflux.example.org 替换为你自己的域名。
使用前将 Caddyfile 中的域名与 $PWD 路径对齐:volumes 中的 $PWD/Caddyfile 表示从当前执行 docker compose 的目录挂载 Caddyfile,因此请从 contrib/docker-compose 目录内运行启动命令,或在 -f 参数基础上同时用 --project-directory 指定正确的项目目录。
方案三:traefik.yml 自动 HTTPS
traefik.yml 使用 Traefik 2.3 作为入口,通过读取 Docker 套接字自动发现并路由服务:
services:
traefik:
image: "traefik:v2.3"
container_name: traefik
command:
- "--providers.docker=true"
- "--providers.docker.exposedbydefault=false"
- "--entrypoints.websecure.address=:443"
- "--certificatesresolvers.myresolver.acme.tlschallenge=true"
- "--certificatesresolvers.myresolver.acme.email=postmaster@example.com"
- "--certificatesresolvers.myresolver.acme.storage=/letsencrypt/acme.json"
depends_on:
- miniflux
ports:
- "443:443"
volumes:
- "./letsencrypt:/letsencrypt"
- "/var/run/docker.sock:/var/run/docker.sock:ro"
miniflux:
image: ${MINIFLUX_IMAGE:-miniflux/miniflux:latest}
container_name: miniflux
depends_on:
db:
condition: service_healthy
expose:
- "8080"
environment:
- DATABASE_URL=postgres://miniflux:secret@db/miniflux?sslmode=disable
- RUN_MIGRATIONS=1
- CREATE_ADMIN=1
- ADMIN_USERNAME=admin
- ADMIN_PASSWORD=test123
- BASE_URL=https://miniflux.example.org
labels:
- "traefik.enable=true"
- "traefik.http.routers.miniflux.rule=Host(`miniflux.example.org`)"
- "traefik.http.routers.miniflux.entrypoints=websecure"
- "traefik.http.routers.miniflux.tls.certresolver=myresolver"
db:
image: postgres:latest
container_name: postgres
environment:
- POSTGRES_USER=miniflux
- POSTGRES_PASSWORD=secret
volumes:
- miniflux-db:/var/lib/postgresql
healthcheck:
test: ["CMD", "pg_isready", "-U", "miniflux"]
interval: 10s
start_period: 30s
volumes:
miniflux-db:
Traefik 侧的关键配置
command 中的参数完成三项工作:
--providers.docker=true启用 Docker Provider;exposedbydefault=false则要求每个服务显式打上traefik.enable=true标签才会被纳入路由,避免误暴露其他容器;--entrypoints.websecure.address=:443定义仅监听 443 的入口,因此宿主机只映射 443 端口;--certificatesresolvers.myresolver.acme.*配置 ACME 证书签发:使用 TLS Challenge(tlschallenge=true),绑定证书签发邮箱postmaster@example.com(请替换为你自己的邮箱),证书存储到/letsencrypt/acme.json,并通过./letsencrypt:/letsencrypt卷持久化到宿主机。
Miniflux 侧的路由标签
Miniflux 服务通过 expose: "8080" 仅暴露内部端口,并依靠四个标签完成路由与证书绑定:
| 标签 | 作用 |
|---|---|
traefik.enable=true |
允许该服务被 Docker Provider 纳入 |
traefik.http.routers.miniflux.rule=Host(\miniflux.example.org`)` |
按域名匹配入站请求 |
traefik.http.routers.miniflux.entrypoints=websecure |
路由绑定到 443 入口 |
traefik.http.routers.miniflux.tls.certresolver=myresolver |
为该路由使用 myresolver 签发 TLS 证书 |
同样需要把 miniflux.example.org 与 postmaster@example.com 替换为真实域名与邮箱,并把 BASE_URL 指向同一域名。
三套方案的选型建议
| 对比项 | basic.yml | caddy.yml | traefik.yml |
|---|---|---|---|
| 对外端口 | 80(HTTP) | 80 + 443 | 443 |
| HTTPS 证书 | 无(如需 HTTPS 需自行配置 TLS) | Caddy 自动签发与续期 | Traefik ACME 自动签发 |
| 反向代理配置 | 无 | Caddyfile 两行即可 | 依赖 Docker 标签 |
| 适用场景 | 内网、局域网、快速体验 | 公网单域名部署,配置最简 | 已有 Traefik 生态或多服务统一入口 |
生产化必须调整的安全项
上述 Compose 文件定位为“示例”,直接照搬用于生产存在明显风险,建议按如下清单调整:
- 修改默认口令:
ADMIN_PASSWORD=test123与数据库密码secret都是占位符,首次登录后应立即在界面中更换管理员密码,并将数据库凭据改为强随机值;更稳妥的做法是使用 Docker secrets 或外部密钥管理,仓库配置解析器也支持DATABASE_URL_FILE之类的 secret 文件形式(见 internal/config/options.go 中secretFileType的处理)。 - 关闭调试日志:basic.yml 中
DEBUG=1便于首启排错,生产环境建议删除或设为0。 - 配置正确的
BASE_URL:无论是否使用反代,都应将BASE_URL设为最终对外的完整地址(含协议与路径),否则订阅链接、OPML 导出等会指向错误地址。 - 固定镜像版本:将
miniflux/miniflux:latest与postgres:latest替换为明确的版本号,避免无意间的破坏性升级。 - 绑定可用卷:确保
miniflux-db(以及 Caddy 的caddy_data/caddy_config、Traefik 的letsencrypt)落盘在持久化存储上,容器重建或宿主机重启后数据与证书不丢失。 - 数据库连接参数:示例使用
sslmode=disable,适用于 Compose 内部网络;若数据库与 Miniflux 不在同一受信网络,应改用sslmode=require或verify-full并配置证书。
常见问题速查
- 容器启动即退出:通常是数据库尚未就绪或迁移失败。确认
db服务健康后再观察docker logs miniflux;迁移报错时可删除miniflux-db卷重新初始化(注意会清空数据)。 - 登录提示用户不存在:
CREATE_ADMIN默认关闭,务必确认环境变量中CREATE_ADMIN=1且ADMIN_USERNAME/ADMIN_PASSWORD已设置;internal/cli/cli.go 只在检测到该选项时才创建管理员。 - 反向代理下页面样式或链接异常:检查
BASE_URL是否与代理入口域名完全一致,包括https://前缀与可能的子路径;internal/config/parser.go 会从BASE_URL推导根地址与基础路径并用于生成链接。 - 证书签发失败:确认域名已解析到服务器公网 IP,且 80/443 端口未被占用或防火墙拦截(TLS Challenge 依赖 443 连通)。
- 健康检查显示 503:就绪探针会 Ping 数据库(internal/http/server/healthcheck.go),数据库不可达时返回 503,请检查
DATABASE_URL与 db 服务状态。
总结
contrib/docker-compose 的三份编排文件覆盖了 Miniflux 容器化部署的完整路径:basic.yml 是最小可用集,适合理解 Miniflux + PostgreSQL 的启动与初始化模型;caddy.yml 以两行 Caddyfile 换来开箱即用的自动 HTTPS;traefik.yml 则展示了基于 Docker Provider 与标签路由的网关式接入。配合仓库源码中对迁移、管理员创建、健康检查与配置解析的实现,你可以快速定位问题、按生产标准加固配置,并在此骨架之上按需扩展。