文本是《Docker(共13篇)》專題的第 11 篇。閱讀本文前,建議先閱讀前面的文章:

往往折騰完一套自動化流程後,總需要在特定情景下推送通知,獲取運行結果,以確認是否成功運行,實現運行錯誤能推送錯誤等。
<a href="https://docs.ntfy.sh/">ntfy</a><em> 允許您使用簡單的 HTTP PUT 或 POST 請求,通過任何電腦的腳本將推送通知發送到您的手機或桌面。</em>
簡介
Ntfy 是一個簡單但功能強大的發布-訂閱(pub-sub)通知服務
通過 HTTP PUT/POST 請求發送通知到任何設備
這是一個自託管的替代方案
可以取代 Line Notify 等服務
功能特性
通知方式多元化:
- 手機推播通知(Android / iOS)
- 電子郵件通知
- WebSocket 即時通知
- Webhook 整合
多種格式支援:
- 純文字訊息
- 結構化 JSON 資料
- 附加檔案傳輸
- 支援 Markdown 格式
安全性功能:
- 訪問控制與身份驗證
- 端到端加密
- 即時訊息傳遞確認
- 支援 HTTPS 加密傳輸
整合能力:
- REST API 支援
- 命令列工具
- 支援 Docker 部署
- 提供各種程式語言的套件(python/Go/PHP/C#/.NET/R/JS/Java/Node)
Ntfy 相較於 Line Notify 的優點
- 開源且可自行架設
- 支援多種通知方式
- 彈性的訂閱方案
- 具備檔案傳輸功能
- API 介面完整
- 不需要辦理帳號即可使用
使用上若是無自己架設的需求
使用官方伺服器路徑https://ntfy.sh/ 即可
想一個不容易被猜到的topic(訊息主題)即可開始享用
其實跟MQTT很像
只是應用的方向不太一樣
有配套的APP使用上是能取代Line Notify
網站網址
電腦端

手機端
安卓

蘋果

安裝教學
ntfy 有個官方實例 ntfy.sh,但是我推薦你自部署一個 ntfy 私有實例,因為官方實例的通知名稱大都被用了,隨機名稱用起來不夠優雅,而且 ntfy 記憶體佔用不大,隨便一個伺服器都能部署。
根據 ntfy 官方文檔 - Installation,大致有三種安裝方式:靜態二進位檔、包管理器、Docker。
這裡我就用包管理器的方法來安裝,之後更新也方便。
因為我的伺服器是 Debian,所以使用下面命令來安裝:
sudo mkdir -p /etc/apt/keyrings
curl -fsSL https://archive.heckel.io/apt/pubkey.txt | sudo gpg --dearmor -o /etc/apt/keyrings/archive.heckel.io.gpg
sudo apt install apt-transport-https
sudo sh -c "echo 'deb [arch=amd64 signed-by=/etc/apt/keyrings/archive.heckel.io.gpg] https://archive.heckel.io/apt debian main' \
> /etc/apt/sources.list.d/archive.heckel.io.list"
sudo apt update
sudo apt install ntfy
sudo systemctl enable ntfy
sudo systemctl start ntfy
你如果伺服器裝了 Docker,當然可以使用 Docker Compose 安裝,這樣管理起來更方便一點。
配置
因為我們是打算部署一個 ntfy 私有實例,所以還需要對 ntfy 的配置檔進行一定的修改。
根據 ntfy 官方文檔 - Configuration,如果你像我一樣使用 Debian 和包管理器安裝 ntfy 的,那麼就要修改位於 /etc/ntfy/server.yml
的配置檔。
/etc/ntfy/server.yml
sudo vim /etc/ntfy/server.yml
基礎設置
base-url
是 ntfy 伺服器的外部 URL,即反代後的訪問位址 。https://ntfy.example.com
listen-http
是 HTTP 監聽位址 。127.0.0.1:2586
⚠️ 注意把 example.com 改成你自己的功能變數名稱。
base-url: "https://ntfy.example.com"
listen-http: "127.0.0.1:2586"
消息快取
ntfy 預設是在記憶體中緩存 12h 的消息,而且實例重啟就丟失了(記憶體特性)。 這裏設置成在硬碟緩存,ntfy 會將消息存儲在基於 SQLite 的緩存,我們設置 24*7=168 h 的持續時間。
cache-file
:快取檔案位址/var/cache/ntfy/cache.db
。cache-duration
:消息在快取中存儲的持續時間168h
。
cache-file: "/var/cache/ntfy/cache.db"
cache-duration: "168h"
訪問控制
設置成私有實例,需要創建使用者和生成訪問令牌來使用,在下文添加使用者、訪問令牌給出了相應操作。
auth-file
:user/access 資料庫位址 ,/var/lib/ntfy/user.db
用於存放使用者和訪問令牌。auth-default-access
:deny-all
匿名使用者的訪問授權 ,即未驗證使用者不能讀寫ntfy主題消息。
auth-file: "/var/lib/ntfy/user.db"
auth-default-access: "deny-all"
通過電子郵件發送通知
這裡就是設置前言中提到的某些服務的通知設置只支援郵件通知的情況下,也可以通過電子郵件發送消息到 ntfy 主題。
⚠️ 注意把 example.com 改成你自己的功能變數名稱。
smtp-server-listen: ":25"
smtp-server-domain: "ntfy.example.com"
smtp-server-addr-prefix: "ntfy-"
值得一提的是,雖然很多雲伺服器商都默認關閉 25 埠的發送,給我們創建域名郵箱設立了門檻,但大多未禁止 25 埠的接收,需要手動放行 25 埠。
之後就可以使用 位址來接收郵件。ntfy-<topic>+<access token>@ntfy.example.com
添加 DNS 記錄
除了修改 /etc/ntfy/server.yml 之外,還需要給你的功能變數名稱 添加 DNS 記錄以實現正常訪問 example.com
。
⚠️ 注意把 example.com 改成你自己的功能變數名稱。
名稱 | 類型 | 值 | 備註 |
ntfy.example.com | A | 公網 IPv4 地址 | 對應 ,是訪問 WebUI 的位址base-url |
ntfy.example.com | MX | ntfy-mx.example.com | 對應 ,是郵寄地址smtp-server-domain |
ntfy-mx.example.com | A | 公網 IPv4 地址 | 和上面的值對應。 |
其實沒有必要添加第三條記錄,將 MX 記錄的值 改成 ntfy-mx.example.comntfy.example.com
就可以了。
反向代理
根據 ntfy 官方文檔 - Behind a proxy,但我沒有用文檔給出的參考配置。
sudo vim /etc/caddy/Caddyfile
ntfy.k1r.in {
encode gzip
tls /home/acme/certs/example.com.fc.crt /home/acme/certs/example.com.pem
reverse_proxy 127.0.0.1:2586
}
sudo caddy fmt --overwrite /etc/caddy/Caddyfile
sudo systemctl reload caddy
文檔給出的配置檔主要是為了將 HTTP 重定向到 HTTPS,讓它在使用 不用添加 前綴,我覺得沒有必要。curl https://
另外,使用 Caddy 在這裡有個好處就是不用額外配置就支援 WebSockets,在 ntfy 用戶端更改連結協定為 WebSockets,相比於“HTTP 傳輸的 JSON 數據流”更加省電(開發者說的,我沒怎麼體會到)。
如果成功配置了反向代理,你現在就可以通過 https://ntfy.example.com
訪問到自部署的 ntfy 私有實例了。
添加使用者、訪問令牌
根據 ntfy 官方文檔 - Users and roles,我們可以使用 來添加使用者。ntfy user
需要注意的是,由於 僅擁有者 ntfy 可寫,使用 命令需要 root 許可權。auth-file: "/var/lib/ntfy/user.db"
ntfy user
⚠️ 可以將 phil 改成你想要的使用者名。
sudo ntfy user add --role=admin phil
sudo ntfy user list
根據 ntfy 官方文檔 - Access tokens,我們可以使用 來添加訪問令牌。ntfy token
原因同上,使用 ntfy token
命令需要 root 許可權。
⚠️ 記得將 phil 改成你的使用者名。
sudo ntfy token add phil
sudo ntfy token list
使用
到此,你已經部署好一個 ntfy 私有實例了。
在官方文檔 - Sending messages 中,已經把發送通知的參數說明清楚了,推薦完整閱讀,我這裡就不贅述了。
接下來就講一下在各種場景中怎麼使用,主要是因為私有實例,需要進行身份驗證。
(1) bash 指令稿(命令行)
curl \
-H "Authorization: Bearer tk_123456" \
-H "Title: Unauthorized access detected" \
-d "Remote access to phils-laptop detected. Act right away." \
https://ntfy.example.com/phil_alerts
身份驗證有兩種方式:
- 存取權杖:
-H "Authorization: Bearer tk_123456"
- 使用者密碼:
-u testuser:fakepassword
注意最後一行的網址要加 前綴,因為前面反向代理並沒有設置將 HTTP 重定向到 HTTPS。https://
(2) Webhook 推送
前面提到,很多服務都集成通知設置,其中大多支援 ntfy,而他們大多都是以 JSON 的格式配置通知的。
還有一些服務(如 Jellyfin Webhook 外掛程式),本身並未集成 ntfy 通知,但是支援通用的 Webhook,也是如下設置。
根據官方文檔 - Publish as JSON:
- URL:
https://ntfy.example.com
- Header:
{
"Authorization": "Bearer tk_123456"
}
- Body:
{
"topic": "phil_alerts",
"title": "Unauthorized access detected",
"message": "Remote access to phils-laptop detected. Act right away."
}
需要注意的是,URL 不能帶上主題,即只能是 https://ntfy.example.com
,不能是 。https://ntfy.example.com/phil_alerts
另外,有些服務的 Webhook 只支援接受一行連結,且發送的消息是 JSON 文本,ntfy 也能將其轉換成可讀的消息,參見文末 Waline 示例。
(3) 通過郵件推送
一些服務(如 TrueNAS Core 的系統警報服務),既沒集成 ntfy 通知,也不支援通用 Webhook,但是支援發送郵件。
電子郵件接收位址為 [email protected]
ntfy-
smtp-server-addr-prefix
是設置的位址前綴。phil_alerts
是你的主題名稱。tk_123456
是你的訪問令牌,前面用+
連接z。ntfy.example.com
是設置的郵件位址smtp-server-domain
。
示例
這裡放一些在我自部署過程中遇到的比較特殊的 ntfy 通知設置。
Jellyfin Webhook
主要涉及 Python jinja 語法,需要修改的話,可以用 .handlebars
後綴保存檔,就會有語法高亮。
acme.sh
根據 acme.sh 專案 Wiki - Set notification for ntfy,使用下面的命令來設置通知。
export NTFY_URL="https://ntfy.sh"
export NTFY_TOPIC="xxxxxxxxxxxxx"
acme.sh --set-notify --notify-hook ntfy
只提供了 URL 和 Topic 的傳入,沒有身份驗證的方式。 查閱對應的代碼得知,其實就是對URL和 Topic 兩個變數進行簡單拼接,所以我們可以通過在 Topic 變數里添加查詢參數對來實現身份驗證。
根據 ntfy.sh 官方文檔 - Query param,可以使用 來完成身份驗證,但這裡的值不是用戶密碼/訪問令牌,而是對二者編碼后的結果。auth
在 Linux 系統可以運行下面的命令(二者取其一即可)。
⚠️ 注意將命令中的 user:passwd
或者 tk_123456
改成你的。
echo -n "Basic `echo -n 'phil:password' | base64`" | base64 | tr -d '='
# 或者对访问令牌
echo -n "Bearer tk_123456" | base64 | tr -d '='
然後就可以使用如下命令設置 acme.sh 的 ntfy 通知。
export NTFY_URL="https://ntfy.example.com"
export NTFY_TOPIC="acmesh?auth=QmFza..."
acme.sh --set-notify --notify-hook ntfy
waline - 使用範本
我博客的評論用的 Waline,它的 Webhook 推送只有一行簡單的連結,如果不加以處理的話,推送的消息就是 JSON 文本,可讀性很差(下面示例我省略了很多東西)。
{
"type": "new_comment",
"data": {
"comment": {
"link": "https://k1r.in",
"mail": "[email protected]",
"nick": "KrDw",
"url": "/posts/build-your-rss-flow/",
"comment": "**2025-02-02 17:00:00 更新**...",
"rawComment": "**2025-02-02 17:00:00 更新**..."
}
}
}
參照官方文檔 - Message templating,我們可以使用 GO 範本對 JSON 數據格式化成可讀的推送,需要設置 ?template=yes
參數。
我這裡就直接給出我的範本了,最好使用 urlencoder 進行編碼。
Title: {{.data.comment.url}} 有新评论了
Title-Encoded: %7B%7B.data.comment.url%7D%7D%20%E6%9C%89%E6%96%B0%E8%AF%84%E8%AE%BA%E4%BA%86
Message:
读者 {{.data.comment.nick}} 在 {{.data.comment.createdAt}} 评论:\n\n{{.data.comment.rawComment}}
Message-Encoded:
%E8%AF%BB%E8%80%85%20%7B%7B.data.comment.nick%7D%7D%20%E5%9C%A8%20%7B%7B.data.comment.createdAt%7D%7D%20%E8%AF%84%E8%AE%BA%EF%BC%9A%5Cn%5Cn%7B%7B.data.comment.rawComment%7D%7D
配合前面 acme.sh 示例中提到的身份驗證查詢參數 auth
,最終得到如下 Webhook 連結(為了你的閱讀體驗我使用了換行,請在實際使用中不要換行):
https://ntfy.example.com/waline
?auth=QmFza...
&markdown=yes
&template=yes
&title=%7B%7B.data.comment.url%7D%7D%20%E6%9C%89%E6%96%B0%E8%AF%84%E8%AE%BA%E4%BA%86
&message=%E8%AF%BB%E8%80%85%20%7B%7B.data.comment.nick%7D%7D%20%E5%9C%A8%20%7B%7B.data.comment.createdAt%7D%7D%20%E8%AF%84%E8%AE%BA%EF%BC%9A%5Cn%5Cn%7B%7B.data.comment.rawComment%7D%7D
進階使用教學
結合Node-Red

[
{
"id": "c956e688cc74ad8e",
"type": "http request",
"z": "fabdd7a3.4045a",
"name": "ntfy.sh",
"method": "POST",
"ret": "txt",
"paytoqs": "ignore",
"url": "https://ntfy.sh/mytopic",
"tls": "",
"persist": false,
"proxy": "",
"authType": "",
"senderr": false,
"credentials":
{
"user": "",
"password": ""
},
"x": 590,
"y": 3160,
"wires":
[
[]
]
},
{
"id": "32ee1eade51fae50",
"type": "function",
"z": "fabdd7a3.4045a",
"name": "data",
"func": "msg.payload = \"Something happened\";\nmsg.headers = {};\nmsg.headers['tags'] = 'house';\nmsg.headers['X-Title'] = 'Home Assistant';\n\nreturn msg;",
"outputs": 1,
"noerr": 0,
"initialize": "",
"finalize": "",
"libs": [],
"x": 470,
"y": 3160,
"wires":
[
[
"c956e688cc74ad8e"
]
]
},
{
"id": "b287e59cd2311815",
"type": "inject",
"z": "fabdd7a3.4045a",
"name": "Manual start",
"props":
[
{
"p": "payload"
},
{
"p": "topic",
"vt": "str"
}
],
"repeat": "",
"crontab": "",
"once": false,
"onceDelay": "20",
"topic": "",
"payload": "",
"payloadType": "date",
"x": 330,
"y": 3160,
"wires":
[
[
"32ee1eade51fae50"
]
]
}
]
// 使用 Base64 編碼中文標題
function encodeHeaderAsRFC2047(text) {
// 轉換成 Base64
const base64 = Buffer.from(text).toString('base64');
return `=?UTF-8?B?${base64}?=`;
}
msg.payload = `測站: ${msg.payload.測站}
⏰ 時間: ${msg.payload.時間}
📊 偵測值: ${msg.payload.偵測值}
⚠️ 預警值: ${msg.payload.預警值}
`;
msg.headers = {};
msg.headers['tags'] = 'rotating_light'; // 加入警告和警報 emoji
msg.headers['X-Title'] = encodeHeaderAsRFC2047('監測警報');
msg.headers['Priority'] = 5; // 設定最高優先級 (5 = max/urgent)
return msg;
Home Assistant
結合到HA也十分方便
notify:
- name: ntfy
platform: rest
method: POST_JSON
data:
topic: YOUR_NTFY_TOPIC
title_param_name: title
message_param_name: message
resource: https://ntfy.sh
收費方案
主要是可以辦帳號用於保存相關的資料

是否可以商用?
最後最重要一點
Ntfy 是以 Apache 許可證 2.0 和 GPLv2 許可證下獲得雙重許可發布的開源軟體
Apache License 2.0 主要特點:
- 完全允許商業使用:
- 可以免費用於商業目的
- 可以修改原始碼
- 可以自由分發
- 可以私有使用
- 主要要求:
- 必須保留原始版權聲明
- 必須包含 Apache License 的副本
- 若修改了代碼,需要明確說明
- 商業應用場景:
- 企業內部部署
- 整合到商業產品中
- 提供託管服務
- 二次開發後商用
- 授權相容性:
- 與大多數開源授權相容
- 可以與專有軟體結合
- 可以改變授權條款
網站網址
您已閱讀完《Docker(共13篇)》專題的第 11 篇。請繼續閱讀該專題下面的文章:
文章標題:ntfy:自建一個簡單易用的消息推送服務
本文鏈接:https://angelal.cc/2005.html
文章版權:除非特別註明,否則均為AngelaL的原創文章,轉載必須以鏈接形式標明本文鏈接
本文最後更新發佈於:2025年03月17日 23:58, 某些文章具有時效性,若有錯誤或已失效,請在下方留言。