ntfy 自建推送服务:Docker 部署、主题权限与消息留存

开源项目1分钟前发布 一方
0 00

ntfy 是一个用 HTTP 发通知的服务。往一个地址 POST 一段文字,订阅了这个主题的手机或桌面就会弹出通知。发送端不需要 SDK,也不需要注册,一条 curl 就够。

它用 Go 写,仓库采用 Apache-2.0 与 GPLv2 双许可,2026 年 11 月 8 日查看时 35,382 个 Star、1,702 个 Fork。当前版本 v2.37.2,2026 年 11 月 4 日发布。

ntfy 是什么:把通知变成一次 HTTP 请求

模型是发布订阅。主题不用预先创建,谁先发布或订阅,它就存在了。因为不需要注册,主题名本身就是凭证——知道名字的人都能读写,这也是官方文档反复提醒"把主题当密码"的原因。

发送有三条路:PUT 或 POST 到 /主题名,请求体就是正文;用请求头带标题、优先级、标签、点击跳转地址;或者 POST 到根路径 /,用一个 JSON 描述整条消息。JSON 方式适合一次带多个字段,比如最多三个操作按钮。

优先级从 1 到 5,对应的提示强度不同:1 静默无声音,3 是默认,5 会弹窗并长震动。附件可以随消息一起发,也可以用 X-Attach 指向一个外部 URL。定时消息用 Delay 头,最短 10 秒,最长 3 天,具体写法见发布文档。

正文上限 4096 字节。超过这个长度,或者内容不是 UTF-8,服务器会自动把它当附件发出去,而不是返回错误。这个行为容易让人以为消息丢了,其实它在客户端显示成一个文件。

Docker 部署:一个容器,两个卷

镜像 binwiederhier/ntfy 提供 amd64、armv6、armv7、arm64 四种架构,老树莓派也能跑。服务把 Web 界面与 API 都放在 80 端口,容器只需要暴露一个端口。以下命令出自安装文档。

docker run 
  -v /var/cache/ntfy:/var/cache/ntfy 
  -p 80:80 
  -it 
  binwiederhier/ntfy 
    serve 
    --cache-file /var/cache/ntfy/cache.db

两个卷分工不同。/var/cache/ntfy 放 SQLite 消息缓存和附件,不给它就会退回纯内存模式,重启即清空;/etc/ntfy 放 server.yml、user.db 与模板,镜像里没有这个文件,要自己在宿主机建好再挂进去。

部署项说明
端口容器内固定 80,映射到宿主机任意端口
/var/cache/ntfycache.db 与附件目录,需要持久化
/etc/ntfyserver.yml、user.db、templates,镜像内不含
内存占用官方 Kubernetes 示例给 128Mi 内存上限,单个 Go 进程
健康检查GET /v1/health 返回 healthy 为 true

生产上建议写成 compose:command 固定成 serve,环境给 TZ,挂上两个目录,再用健康检查加 restart 策略兜底。文档里有句提醒容易被跳过:用非 root 用户跑时,cache 目录、user.db、附件目录都要 chown 到同一个 uid,否则写不进去,症状是消息能发但重启后全部消失。

对外要套 HTTPS。手机端订阅的是完整 URL,明文 HTTP 在系统看来就是一条可被中间人读写的通道,也拿不到 Web Push 需要的安全上下文。

主题与权限:先当密码用,再收口

主题名只允许字母、数字、下划线与连字符,最长 64 个字符。这个限制不是洁癖,是为了让主题能安全地出现在 URL 路径里,输入其他字符会被直接去掉。

公开实例的默认姿态是开放的:任何人可读写任何主题。要变成私用实例,靠 auth-file 这一个开关。设置它即开启认证,用户、密码哈希、角色、ACL 都落在那个 SQLite 文件里。

关键的一步是 auth-default-access。默认值 read-write 意味着没有匹配 ACL 的访问仍然放行;改成 deny-all,才真正变成"有授权才能进"。很多人配了用户却没改这一项,结果实例还是敞开的。

权限分四种:read-write、read-only、write-only、deny。ACL 支持用 * 通配主题,例如 alerts_* 一次覆盖一整组。角色只有 user 与 admin,admin 跳过逐条 ACL。命令行里的 ntfy user 与 ntfy access 可以直接管,先在测试机上把规则摸清再上生产,比改配置文件快。

还有一个常被忽略的组合:访问令牌。token 以 tk_ 开头,每用户最多 60 个,可以设过期时间与标签。当前 token 授予的是该账户的完整权限,做不到"只读 token"或"只能发某主题的 token",所以别把它塞进不该有读权限的脚本里。

主题命名还有一层现实风险:因为不注册即可用,猜中名字就能订阅。机器生成的随机后缀比 backup、test 这类词靠谱得多,这一点和把服务做成常驻 systemd 单元时的口令管理是一个道理,能用随机值就别用人想出来的词。

消息留存与限流:12 小时和几个默认值

缓存时长默认 12 小时,可用 cache-duration 改;设成 0 就完全不留,只推给在线订阅者。这一项决定了手机离线一段时间之后,还能不能补收到中间的消息。

配置项默认值含义
cache-duration12h消息在缓存里保留多久,0 表示不缓存
attachment-file-size-limit15M单个附件上限
attachment-total-size-limit5G附件目录总量上限
attachment-expiry-duration3h附件过期删除时间,且不超过消息存活期
global-topic-limit15,000实例允许的主题总数
visitor-request-limit-burst60每访客请求令牌桶容量
visitor-request-limit-replenish5s每 5 秒补一个请求
visitor-attachment-total-size-limit100M每访客附件占用总量
auth-default-accessread-write没有 ACL 匹配时的回退权限

附件那三个值要一起看。附件 3 小时后被删,且不会活得比消息更久,所以它是"送达即用"的通道,不是文件柜。想长期存图,得自己另存一份。

限流的单位是访客,而访客默认按 IP 识别。服务跑在反向代理后面时,必须打开 behind-proxy,让它从 X-Forwarded-For 取真实地址;不开的话,所有请求被当成同一个访客,共用一个令牌桶,很快就会开始返回 429。

IPv6 还有一层细节。第三方实践记录里提到,默认按 /64 子网聚合访客,同一段里的机器算一个人。云主机上如果这一段被邻居共用,配额会比预期更快见底,这也是有人自建之后才发现的落差。完整参数与说明在配置文档里。

客户端:安卓直接连,iOS 要绕一道

安卓端在 Google Play 与 F-Droid 都有,iOS 版在 App Store,源码分别在 ntfy-android 与 ntfy-ios 两个仓库。桌面端不用装东西,打开 Web 应用订阅主题,浏览器保持连接并弹系统通知。

真正的差别在 iOS。系统严格限制后台执行,非官方签名的应用没法自己维持长连接,所以自建服务器要开 upstream-base-url 指向 ntfy.sh:有新消息时,你的服务器只把消息 ID 与主题 URL 的哈希发给上游,上游经 Firebase、APNS 唤醒手机,手机再回到你自己的服务器取正文。

这带来两个后果。正文没有经过第三方,但"什么时候有消息、对应哪个主题的哈希"对上游可见;不配这一项也能收到,只是延迟从几秒变成几十分钟到几小时不等,取决于手机状态。如果这件事不能接受,就得接受 iOS 上不即时这个前提。

安卓侧还有一个与Beszel 这类轻量监控搭配的常见用法:监控端把告警推到 ntfy,手机端只负责收,不需要在监控系统里配邮件服务器。整个链路只有一次 HTTP 请求,排查起来也简单。

自建与 ntfy.sh 的差别,以及适合谁

官方服务按档位给额度:Supporter 每天 2,500 条消息、3 个保留主题;Pro 每天 2 万条;Business 每天 5 万条,附件上限也随档位提高。自建不是"把额度调高",而是上面那张表里的每一项都变成 server.yml 的配置,由自己定义。

代价是运维。证书、反向代理、备份、升级、账号体系都得自己管;缓存是 SQLite 单文件,单机结构,横向扩展不是它擅长的方向。机器很闲的话,一个容器加一个免费证书就是全部成本,和Coolify 这类自托管部署平台上一键起的服务没什么区别。

适合谁:手上有服务器、需要把告警接到手机上的人——定时任务失败、备份完成、门磁触发、CI 构建结束、下载器跑完。也适合把通知当内部事件总线的小团队,一个主题一个用途,命名里带环境前缀。

只想偶尔给自己发一条提醒,直接用 ntfy.sh 更快,先验证需求再自建。反过来,如果某条通知"没收到会很麻烦",那就别把这条链路架在免费公共服务的配额上。判断标准不是功能,是这条消息值不值得一台机器。


相关地址

以下为文中项目的官方入口,版本号、参数默认值与部署方式以这些页面为准。

  • ntfy(GitHub 仓库):https://github.com/binwiederhier/ntfy
  • ntfy(官网与定价):https://ntfy.sh/
  • ntfy(文档首页):https://docs.ntfy.sh/
  • ntfy Android 客户端(GitHub):https://github.com/binwiederhier/ntfy-android
  • ntfy iOS 客户端(GitHub):https://github.com/binwiederhier/ntfy-ios
© 版权声明

相关文章