ntfy 是一套開源推播通知伺服器,只要對 https://你的網域/主題名 發一個 HTTP POST,訊息就會出現在手機或桌面。它用 Apache-2.0/GPLv2 雙授權、GitHub 3.5 萬星,Docker 映像檔 35MB、閒置只吃約 15MB 記憶體,是自架監控與腳本通知最省事的選擇。
為什麼要為「通知」這件事自架一個服務
多數人第一條通知是 Telegram bot:免費、五分鐘搞定、手機一定收得到。問題會在第三個月出現:
| 痛點 | 實際會遇到的情況 |
|---|---|
| 平台風險 | 通知服務本身被關掉、API 改版、bot token 政策變動。國內常見的 LINE Notify 就是例子——它在 2025 年 3 月底終止服務(以官方公告為準),當時一堆排程腳本一夜失效 |
| 內容外流 | 你的告警訊息會經過第三方伺服器,裡面常有主機名、內網 IP、資料庫連線字串片段 |
| 額度與限速 | 服務掛掉時告警會「爆量」,免費 bot 很容易撞到每分鐘上限,最需要通知的時候剛好被擋 |
| 機構網路 | 某些企業或校園網路直接封鎖 Telegram,工程師在家測得通、進公司就收不到 |
| 格式受限 | 想帶圖片(例如監視器抓到的畫面)、想放「重開服務」按鈕,多數 bot API 做得不直覺 |
ntfy 走的是完全不同的路線:它只是一個 HTTP 端點。不需要 SDK、不需要註冊應用程式、不需要管 token 政策,curl 就是完整功能的客戶端。這件事在維運場景特別重要——你半夜三點在修機器時,不會想先讀 API 文件。
一表看懂:ntfy 到底提供什麼
| 項目 | 內容 | 備註 |
|---|---|---|
| 專案 | binwiederhier/ntfy | 2021 年建立,至今持續活躍 |
| 星數 | 35,187 | GitHub API 查詢,2026-10-30 |
| 授權 | Apache-2.0 與 GPLv2 雙授權 | 自架商用沒問題 |
| 最新版 | v2.35.0(2026-10-30 發布) | 本文實測版本 |
| 映像檔大小 | 35.0 MB | docker image inspect 實測 |
| 閒置記憶體 | 14.79 MiB | docker stats 實測 |
| 訊息長度上限 | 4,096 bytes | 超過自動轉為附件 |
| 附件上限 | 單檔 15MB、每位訪客 100MB、總計 5GB | 自架預設值;ntfy.sh 免費為單檔 2MB |
| 附件保存 | 3 小時 | attachment-expiry-duration |
| 訊息快取 | 12 小時 | 支援 since= 補抓與斷線重連 |
| 費率限制 | 每位訪客 60 次請求的桶子,每 5 秒補 1 次 | 可調 |
| 平台 | Android、iOS、Web、CLI、任何能發 HTTP 的東西 | Android 有 F-Droid 版(不用 Firebase) |
| 資料庫 | 預設 SQLite;可換 PostgreSQL | 換 PostgreSQL 會順帶啟用認證 |
(以上數字除特別標註「實測」者外,以官方文件與 GitHub API 於 2026-10-30 的顯示為準。)
運作流程:為什麼它「不用裝任何東西」也能推播
| 角色 | 做什麼 | 對應動作 |
|---|---|---|
| 發布端 | 對 POST /<topic> 送出訊息,可帶標題、優先級、標籤、附件、按鈕 | curl、Python、Shell、CI、監控系統 webhook |
| 伺服器 | 驗證權限 → 寫入快取 → 推給目前連線的訂閱者 | 你的 ntfy 容器 |
| 訂閱端 | 長輪詢或 SSE 收訊息;App 收到後交給系統通知 | 手機 App、Web UI、CLI |
| 系統推播 | Android 走 FCM、iOS 走 APNs、瀏覽器走 Web Push | 這是「手機關螢幕也收得到」的關鍵 |
有一個關鍵設計要知道:主題(topic)本身就是頻道,訂閱與發布都用同一個名字,不存在「先建立頻道」這一步。所以在 ntfy.sh 上免註冊使用時,主題名就等於密碼——取什麼 test、alert 這種名字,全世界都猜得到也訂得到。
5 分鐘先試用:不架站也能玩
先用官方公用伺服器確認你要的通知長什麼樣子。手機裝好 App(Google Play/F-Droid/App Store)→ 訂閱一個只有你知道的名字:
curl -d "伺服器重開完成" ntfy.sh/most-tw-demo-8f3k2
加上標題、優先級與 emoji 標籤只要多幾個 header:
curl \
-H "Title: 備份完成" \
-H "Priority: high" \
-H "Tags: white_check_mark,floppy_disk" \
-d "restic snapshot ok,耗時 4 分 12 秒" \
ntfy.sh/most-tw-demo-8f3k2
優先級共五級(1/2/3/4/5,或 min/low/default/high/urgent)。它決定手機要不要響、要不要震、要不要彈出橫幅——這件事比多數人想像的重要:全部設 urgent 的結果就是全部被無視。
想帶圖片(例如監視器畫面)就把檔案 PUT 上去:
curl -T snapshot.jpg \
-H "Filename: snapshot.jpg" \
-H "Message: 前門偵測到人" \
ntfy.sh/most-tw-demo-8f3k2
公用伺服器的免費額度(依官方文件):每天 250 則訊息、附件單檔 2MB、每位訪客每日 200MB 流量。拿來測試夠用,拿來當正式告警不夠——所以下面開始自架。
自架:一份 compose 就跑起來
官方映像檔是 binwiederhier/ntfy,啟動參數固定 serve。這份是實際測試過的設定(裸機 Docker,無反向代理,先把認證與快取打開):
services:
ntfy:
image: binwiederhier/ntfy:v2.35.0
restart: unless-stopped
command: serve
environment:
NTFY_BASE_URL: http://localhost:2586
NTFY_LISTEN_HTTP: ":80"
NTFY_CACHE_FILE: /var/lib/ntfy/cache.db
NTFY_AUTH_FILE: /var/lib/ntfy/user.db
NTFY_AUTH_DEFAULT_ACCESS: deny-all
NTFY_BEHIND_PROXY: "true"
NTFY_ATTACHMENT_CACHE_DIR: /var/lib/ntfy/attachments
NTFY_ENABLE_LOGIN: "true"
volumes:
- ./data:/var/lib/ntfy
ports:
- "2586:80"
healthcheck:
test: ["CMD-SHELL", "wget -q --spider http://127.0.0.1:80/v1/health || exit 1"]
interval: 10s
timeout: 3s
retries: 5
啟動後驗證:
docker compose config --quiet # 先驗語法,錯字在這裡就會被抓到
docker compose up -d
curl -s http://127.0.0.1:2586/v1/health
# {"healthy":true,"role":"active"}
實測結果:容器約 11 秒進入 healthy,映像檔 35.0MB,閒置記憶體 14.79 MiB,Web UI(http://主機:2586/)回應 200。資料落地只有三個東西——cache.db(訊息快取)、user.db(帳號與 ACL)、attachments/(附件),全部在 ./data 底下,備份就是備份這個資料夾。
⚠️ NTFY_AUTH_DEFAULT_ACCESS: deny-all 是這份設定最重要的一行。沒設的話預設是 read-write,任何人都能對任何主題發布——那等於把一個公開的推播 API 掛在網路上。
私有化:帳號、ACL、Token 三件事
ntfy 的權限模型很簡單:兩種角色(user/admin)× 每個主題的權限(read-write/read-only/write-only/deny),外加一個代表匿名存取的 *。
# 建管理員(用環境變數給密碼,避免互動式輸入)
docker exec -e NTFY_PASSWORD='你的密碼' ntfy ntfy user add --role=admin admin
# 建一個只能寫 backups 主題的機器人帳號
docker exec -e NTFY_PASSWORD='另一個密碼' ntfy ntfy user add backup-script
docker exec ntfy ntfy access backup-script backups rw
# 發一把長期 token(給 Grafana、Uptime Kuma 這類不能存密碼的整合用)
docker exec ntfy ntfy token add --label "grafana" admin
實測行為(本機 Docker,deny-all 設定):
| 動作 | 結果 |
|---|---|
| 匿名 POST 到任意主題 | 403(正確被擋) |
backup-script POST 到自己沒權限的主題 | 403 |
backup-script POST 到 backups | 200 |
用 Authorization: Bearer tk_… 發布 | 200 |
用 admin 帳密輪詢 /backups/json?poll=1&since=all | 回傳含 title、priority、tags 的 JSON |
輪詢回來的 JSON 長這樣(實際輸出,主題與時間已換成示例):
{"id":"AbU0WGO71AVD","time":1793390702,"expires":1793433902,"event":"message",
"topic":"backups","title":"備份完成","message":"restic snapshot ok: 12.4GB",
"priority":4,"tags":["white_check_mark","backup"]}
expires 是 time 加 12 小時,正好對上預設的 cache-duration。這代表「手機離線一段時間後回來,還能把漏掉的訊息補收」——這是 ntfy 相對純推播服務的實質差異。
附件也順手驗了:PUT 一個 16 bytes 的文字檔,回傳的 JSON 會多一個 attachment 物件,內含 name、type、size,以及一個 /file/<id>.txt 的網址,到期時間是 3 小時後(1793401503 - 1793390703 = 10800 秒)。
反向代理與 HTTPS:這步不能省
推播本身走 HTTP 沒問題,但附件、Web UI 登入、Web Push 全部需要有效的 HTTPS(瀏覽器的 service worker 不接受自簽憑證)。三條路都已經有現成教學:
- 用 Caddy 最省事,兩行 Caddyfile 就會自動申請憑證 → Caddy 完整教學
- 想用 Docker label 自動路由 → Traefik 完整教學
- 連 80/443 都不想開(家用 NAS、社區網路)→ Cloudflare Tunnel 完整教學
用反向代理時,NTFY_BASE_URL 一定要填對外網址(https://ntfy.你的網域),並把 NTFY_BEHIND_PROXY 設 true,否則費率限制會把所有請求都算在同一個來源 IP 上——自己人先被自己擋掉。
Caddy 的設定大概長這樣:
ntfy.你的網域 {
reverse_proxy 127.0.0.1:2586
}
手機端:Android 很順,iOS 有一個坑
| 平台 | 取得方式 | 要注意的事 |
|---|---|---|
| Android | Google Play/F-Droid/GitHub Releases APK | F-Droid 版不含 Firebase,所有訂閱預設就是即時;Play 版若想即時,要開「instant delivery」(會有一個常駐通知,背景連線耗電約 0–1%/17 小時) |
| iOS | App Store | 自架伺服器要即時收到,必須設 upstream-base-url: https://ntfy.sh |
| 桌面/瀏覽器 | Web UI + Web Push | 自架要先生成 VAPID 金鑰並設定 web-push-* |
iOS 那個坑值得展開,因為它是很多人「自架後覺得 iOS 很慢」的真正原因。ntfy 的 iOS App 是靠 Apple 推播(APNs)叫醒的,而 APNs 只有 Apple 認得的推播服務能送。官方解法是讓你的自架伺服器在收到訊息時,只把「訊息 ID」丟給 ntfy.sh,再由 App 回頭跟你自己的伺服器拉內容——訊息本文不經過 upstream。設定只有一行:
NTFY_UPSTREAM_BASE_URL: https://ntfy.sh
不設定的話,官方文件的說法是「通知最終還是會到,但可能要幾小時,取決於手機狀態;如果你正在用手機,通常不超過 20–30 分鐘」。對告警來說,20 分鐘跟沒收到差不多。
Web Push(瀏覽器關掉也收得到)要另外備金鑰:
docker exec ntfy ntfy webpush keys # 產生 VAPID keypair
把產出的公鑰/私鑰、web-push-file、web-push-email-address 填進設定即可。這組金鑰只服務「同一個 origin」,不能拿 A 站的金鑰去訂 B 站。
實戰:五個可以直接抄的接法
1. 接 Uptime Kuma(服務掛掉)
Uptime Kuma 內建 ntfy 通知類型,只要填伺服器網址、主題、優先級,需要認證時填帳密或 token。設定細節在站上的 Uptime Kuma 完整教學。
2. 接 changedetection.io(降價、補貨、公告)
changedetection.io 的 ntfy:// 通知格式是官方整合清單第一項,適合拿來盯商品頁與政府公告,寫法見 changedetection.io 完整教學。
3. 接 Grafana 告警
Grafana 的 contact point 選 webhook,指向 https://ntfy.你的網域/grafana-alerts,並在 ntfy 端開一個只給 Grafana 用的 token。這樣 Prometheus 的門檻值告警、Loki 的錯誤率告警都會落到同一支手機。整套堆疊見 Grafana+Prometheus+Loki 完整教學。
4. 接 Frigate(監視器有人就推一張圖)
Frigate 偵測到人/車時可以同時發通知與附上截圖,ntfy 的附件功能在這裡很實用(記得自架的 15MB 上限比 ntfy.sh 的 2MB 寬鬆很多)。設定見 Frigate NVR 完整教學。
5. 接備份腳本(最少人做、最有價值的一件事)
備份最常見的失敗不是備份失敗,是沒人知道它失敗了。borgmatic 與 restic 都有現成的 ntfy 掛法,最土法煉鋼的版本是這樣:
#!/usr/bin/env bash
set -o pipefail
TOPIC="https://ntfy.你的網域/backups"
TOKEN="tk_xxxxxxxxxxxx"
if restic backup /data --tag nightly 2>&1 | tail -3 > /tmp/restic.log; then
curl -s -H "Authorization: Bearer $TOKEN" \
-H "Title: 備份成功" -H "Tags: white_check_mark" -H "Priority: low" \
--data-binary @/tmp/restic.log "$TOPIC"
else
curl -s -H "Authorization: Bearer $TOKEN" \
-H "Title: 備份失敗" -H "Tags: rotating_light" -H "Priority: 5" \
--data-binary @/tmp/restic.log "$TOPIC"
fi
搭配 restic/Kopia/Duplicati 備份完整教學 的 3-2-1 策略,這支腳本就是「備份有沒有真的跑」的唯一證據。
順帶一提:ntfy 的發布端不限 curl。Python 一行 requests.post()、CI 的 curl step、Cron 的 &&/||、Home Assistant 的官方整合、甚至巴法雲這類中國平台的 webhook 都能接。
六套方案比較:什麼時候不該用 ntfy
| 方案 | 型態 | 自架 | 手機端 | 授權 | 適合誰 | 明顯缺點 |
|---|---|---|---|---|---|---|
| ntfy | 推播伺服器 | ✅ | Android/iOS/Web/CLI | Apache-2.0+GPLv2 | 想要一個 HTTP 端點打通所有腳本與監控 | iOS 自架要借道 upstream;公用伺服器主題名即密碼 |
| Gotify | 推播伺服器 | ✅ | 只有 Android 官方 App | 授權檔非標準 SPDX(GitHub 判定 NOASSERTION) | 純 Android 團隊、想更單純 | 沒有官方 iOS App,這是最大分水嶺 |
| Bark | 推播服務(可自架) | ✅ | iOS 為主 | MIT | 全 Apple 生態、想要極簡 | Android 支援弱、功能比 ntfy 少 |
| Apprise | 通知函式庫 | —(不是伺服器) | 看你接的後端 | BSD-2-Clause | 已經有一堆通知管道,想統一呼叫介面 | 它自己不推播;是「翻譯層」,仍需後端 |
| Pushover | 商業 SaaS | ❌ | Android/iOS/Desktop | 閉源 | 想一次買斷、不想維運 | 每個平台 US$4.99 一次性、免費每月 10,000 則;資料經過第三方 |
| Telegram Bot | 商業 SaaS | ❌ | 全平台 | 閉源 | 個人小專案、臨時用 | 企業/校園網路常被封;內容與主機資訊都經過第三方 |
一句話選型:Android 為主、想要最單純 → Gotify;全 Apple 生態 → Bark;要跨平台又要自架 → ntfy;不想維運 → Pushover;已經有一堆管道要統一 → Apprise。
已知限制與還沒解決的坑
寫在前面:以下是官方文件明列的,加上實測或查證時發現的,不是「聽說」:
- iOS 自架的即時性有條件:沒有
upstream-base-url時,延遲可能是幾十分鐘到數小時。這是架構限制,不是 bug。 - Android 的即時送達要付代價:Play 版開 instant delivery 會有一個常駐通知(可關),F-Droid 版則無法關閉(因為它本來就不走 FCM)。
- 主題名在公用伺服器上等於密碼:想保留主題名(避免被別人搶走)要買 ntfy Pro;自架則沒有這個問題,但沒有「主題已被佔用」的概念。
- 附件與訊息都有存活期:訊息 12 小時、附件 3 小時。想拿 ntfy 當「訊息資料庫」會失望,它的定位是通知。
- 費率限制的預設值不算寬:60 次請求的桶子、每 5 秒補 1 次、每位訪客 30 條訂閱連線。監控系統在大量服務同時掛掉時容易撞到,建議自架時把
visitor-request-limit-burst調高。 - PostgreSQL 是「換掉整個儲存層」:設了
database-url後,cache-file/auth-file/web-push-file都必須拿掉,而且會隱含啟用認證。搬家時很容易漏。 - 實驗性叢集不要上生產:新版 server.yml 有
experimental-cluster-*系列選項(需共用 PostgreSQL),官方自己寫明「dropped node-to-node batches are not recovered yet,且費率限制是各節點獨立計算」。 - 本文沒有實測的部分:iOS/Android App 的實際推送延遲、FCM/APNs 的送達率、Web Push 在 Safari 的行為、以及 ntfy Pro 的實際額度。這些需要實機與真實帳號,本文只引用官方文件說明。
誰適合、誰不需要
適合:已經在自架監控(Uptime Kuma、Grafana、Frigate、changedetection.io)的人;有一堆 cron/CI 腳本想統一發通知的人;在意告警內容不該經過第三方的人;公司網路擋 Telegram 的人。
不需要:只有一個小專案、一個月幾十則通知 → Telegram bot 更省事;完全不想維運 → Pushover 一次買斷比養容器便宜;需要「通知+值班排程+升級通報」的企業流程 → 那是 PagerDuty/Grafana OnCall 這類工具的責任,ntfy 只是最後一哩。
站內延伸閱讀
- Uptime Kuma 完整教學 2026:91K 星自架監控
- Grafana+Prometheus+Loki 完整教學 2026
- changedetection.io 完整教學 2026:網頁變更監控
- Docker Compose 完整教學 2026
- Caddy 完整教學 2026:自動 HTTPS 反向代理
- Frigate NVR 完整教學 2026:AI 監視器
原始來源
- 官方文件:ntfy docs、安裝、伺服器設定、發布參數、手機訂閱、整合清單
- 官方範例設定:server.yml
- 專案首頁與版本:ntfy.sh、GitHub Releases
- 比較對象數據:GitHub API(gotify/server、Finb/Bark、caronc/apprise,2026-10-30 查詢)、Pushover 定價
本文的 Docker 設定、權限行為與效能數字為 2026-10-30 在本機以 v2.35.0 實際執行所得;星數、授權與版本以查證當日官方頁面為準。文中無聯盟連結。
