SSD Nodes Learn Hosting plans →
गाइड Matt Connorलेखक: Matt Connor · अपडेट किया गया: 2026-08-27

Docker पर self-hosted ntfy server कैसे सेटअप करें

Docker Compose का उपयोग करके अपने VPS पर ntfy को TLS के साथ सुरक्षित रूप से इंस्टॉल करें। ACLs और users के साथ topics को लॉक करें और cron या systemd OnFailure से अलर्ट पाएं।

Self-hosted ntfy server क्या करता है

एक self-hosted ntfy server HTTP POST को आपके फोन पर push notification में बदल देता है। आप curl का उपयोग करके publish करते हैं, और संदेश Android app, iOS app, browser tab, या किसी भी ऐसी चीज़ पर पहुँच जाता है जो HTTP connection को open रख सकती है। इसमें न तो कोई client library install करने की आवश्यकता है और न ही कोई message broker चलाने की।

ntfy संदेशों को topic के आधार पर संबोधित करता है। Topic URL path में एक नाम होता है, जैसे https://ntfy.example.com/alerts, और यह उस क्षण अस्तित्व में आ जाता है जब कोई उस पर publish करता है। डिफ़ॉल्ट install पर, जो कोई भी उस नाम को जानता है वह topic को पढ़ सकता है और उस पर लिख सकता है, यही कारण है कि प्रोजेक्ट का अपना documentation topic के नाम की तुलना password से करता है। यह मॉडल सार्वजनिक ntfy.sh सेवा के लिए ठीक है। यह उस सर्वर के लिए ठीक नहीं है जो आपके backup failures की जानकारी ले जा रहा हो, इसलिए यह गाइड पहला संदेश भेजे जाने से पहले ही authentication को चालू कर देती है।

शुरू करने से पहले आपकी आवश्यकताएं

आपको Ubuntu 24.04 या Debian 13 पर चलने वाला एक VPS चाहिए, जिसमें Docker Engine और Compose plugin इंस्टॉल हो। इसके अलावा एक domain name और बहुत कम RAM की आवश्यकता है। एक DNS (domain name system) A record बनाएं जो ntfy.example.com को सर्वर के public IP address पर point करे, और किसी भी अन्य काम को करने से पहले यह सुनिश्चित करें कि यह resolve हो रहा है।

dig +short ntfy.example.com
sudo ufw allow 80,443/tcp
sudo ufw status

dig को आपके सर्वर का IP print करना चाहिए। यदि यह कुछ भी print नहीं करता है, तो certificate जारी करने की प्रक्रिया विफल हो जाएगी, क्योंकि certificate authority बाहर से नाम की जांच करती है। Port 80 खुला रहना चाहिए क्योंकि ACME (automatic certificate management environment), जो Let's Encrypt के पीछे का protocol है, इसका उपयोग HTTP challenge के लिए करता है। ntfy container को स्वयं कभी भी public port नहीं मिलता है।

ntfy कॉन्फ़िगरेशन फ़ाइल लिखें

Docker image में कोई कॉन्फ़िगरेशन फ़ाइल नहीं होती है, इसलिए आपको एक बनानी होगी। इस गाइड में बाद में आने वाली हर कमांड इसी से पढ़ती है। सबसे पहले वह user ID और group ID पता करें जिसके रूप में container चलेगा।

id -u
id -g
sudo install -d -o "$(id -u)" -g "$(id -g)" /etc/ntfy /var/cache/ntfy /var/lib/ntfy
sudo nano /etc/ntfy/server.yml
base-url: "https://ntfy.example.com"
listen-http: ":2586"
behind-proxy: true
cache-file: "/var/cache/ntfy/cache.db"
cache-duration: "12h"
auth-file: "/var/lib/ntfy/user.db"
auth-default-access: "deny-all"
enable-login: true
enable-signup: false

उनमें से चार पंक्तियाँ सबसे महत्वपूर्ण हैं। base-url बिल्कुल सटीक public HTTPS पता होना चाहिए, क्योंकि ntfy इसी से attachment links और web app के अपने requests बनाता है। गलत मान होने पर web app लोड तो हो जाएगा, लेकिन कोई भी action पूरा नहीं कर पाएगा। listen-http: ":2586" container के अंदर सभी interfaces से bind होता है। यह देखने में लापरवाही लग सकता है, लेकिन यह सही है: container का अपना network namespace होता है, इसलिए वहाँ 127.0.0.1 से bind करने पर port host से unreachable हो जाएगा और Docker का published port कभी connect नहीं हो पाएगा। auth-default-access: "deny-all" पूरी सुरक्षा व्यवस्था है, क्योंकि यह स्पष्ट अनुमति के बिना किसी को भी read और write करने से रोकता है। behind-proxy: true ntfy को निर्देश देता है कि वह client का पता X-Forwarded-For header से ले, ताकि rate limits reverse proxy को एक व्यस्त client गिनने के बजाय वास्तविक visitors की गिनती करें।

enable-login: true web app और phone apps को password के साथ sign in करने की सुविधा देता है। enable-signup को false ही रहने दें, क्योंकि private server पर self-service account creation एक खुला दरवाजा है जिसमें बस कुछ अतिरिक्त कदम होते हैं।

sudo chown "$(id -u):$(id -g)" /etc/ntfy/server.yml
sudo chmod 600 /etc/ntfy/server.yml

Docker Compose के साथ ntfy चलाएं

इसे /opt/ntfy/compose.yaml में रखें और 1000:1000 को ऊपर दिए गए दो नंबरों id -u और id -g से बदलें।

services:
  ntfy:
    image: binwiederhier/ntfy:v2.27.0
    container_name: ntfy
    command: serve
    user: "1000:1000"
    environment:
      - TZ=UTC
    volumes:
      - /etc/ntfy:/etc/ntfy
      - /var/cache/ntfy:/var/cache/ntfy
      - /var/lib/ntfy:/var/lib/ntfy
    ports:
      - "127.0.0.1:2586:2586"
    restart: unless-stopped
cd /opt/ntfy
sudo docker compose up -d
sudo docker compose logs ntfy
curl -s http://127.0.0.1:2586/v1/health

स्वस्थ server {"healthy":true} का उत्तर देता है। उस compose file में दो विवरण जानबूझकर इस तरह रखे गए हैं। Image को v2.27.0 पर pin किया गया है, जो August 2026 तक का current release है, न कि latest पर। कारण यह है कि latest के साथ अगला docker compose pull आपके server का version बदल देता है और आपको इसका पता बाद में changelog से चलता है। Port को 127.0.0.1:2586:2586 के रूप में publish किया गया है, इसलिए container केवल host के loopback address से reachable है। इसके बजाय 2586:2586 लिखने पर Docker अपने firewall rules आपके rules से पहले insert कर देता है। इसका अर्थ है कि port internet से उत्तर देने लगता है, भले ही ufw status कहता हो कि port बंद है। ये दोनों आदतें आपके द्वारा जोड़े जाने वाले अगले container पर भी लागू होती हैं: self-hosted RustDesk relay अपनी image tag को इसी तरह pin करता है, लेकिन वह loopback के पीछे नहीं रह सकता, क्योंकि उसके signal और relay ports को internet से उत्तर देना आवश्यक है।

यदि curl Connection refused प्रिंट करता है, तो container log पढ़ें। /var/lib/ntfy/user.db पर permission error का मतलब है कि user: लाइन उन directories के owner से मेल नहीं खाती है, इसलिए process अपना database नहीं बना पाती और बंद हो जाती है। VPS के लिए Docker Compose की बुनियादी मार्गदर्शिका volume ownership और restart policies के बारे में अधिक विस्तार से जानकारी देती है।

Caddy के साथ TLS का उपयोग करें

Caddy स्वयं certificate का अनुरोध और नवीनीकरण करता है, जो TLS (transport layer security) को कार्यशील बनाने का सबसे छोटा रास्ता है।

sudo apt install -y debian-keyring debian-archive-keyring apt-transport-https curl
curl -1sLf 'https://dl.cloudsmith.io/public/caddy/stable/gpg.key' | sudo gpg --dearmor -o /usr/share/keyrings/caddy-stable-archive-keyring.gpg
curl -1sLf 'https://dl.cloudsmith.io/public/caddy/stable/debian.deb.txt' | sudo tee /etc/apt/sources.list.d/caddy-stable.list
sudo chmod o+r /usr/share/keyrings/caddy-stable-archive-keyring.gpg
sudo chmod o+r /etc/apt/sources.list.d/caddy-stable.list
sudo apt update
sudo apt install -y caddy

/etc/caddy/Caddyfile की सामग्री को तीन लाइनों से बदलें।

ntfy.example.com {
    reverse_proxy 127.0.0.1:2586
}
sudo systemctl reload caddy
curl -s https://ntfy.example.com/v1/health

HTTPS पर वही {"healthy":true} का अर्थ है कि पूरा पाथ सही ढंग से काम कर रहा है। Caddy से मिलने वाला 502 यह दर्शाता है कि ntfy listening मोड में नहीं है: इसे sudo ss -lntp | grep 2586 के साथ चेक करें। certificate error का सामान्य अर्थ है कि DNS रिकॉर्ड गलत है या port 80 ब्लॉक है, और sudo journalctl -u caddy -n 50 यह बताता है कि समस्या कहाँ है। बाद में कोई अन्य सर्विस जोड़ने के लिए उसी Caddyfile में एक और hostname ब्लॉक की आवश्यकता होती है, और इसी तरह Halcyon, a 90s video store front end for your Jellyfin library जैसी चीजें उसी बॉक्स के दूसरे subdomain पर चलती हैं।

यदि आप पहले से ही nginx चला रहे हैं, तो ntfy द्वारा प्रलेखित proxy सेटिंग्स को कॉपी करें: proxy_http_version 1.1, proxy_buffering off, proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for, और कम से कम तीन मिनट का read और send timeout सेट करें। एक subscriber जब तक listening मोड में रहता है, तब तक एक HTTP कनेक्शन खुला रखता है, और nginx डिफ़ॉल्ट रूप से 60 सेकंड के बाद idle upstream कनेक्शन को बंद कर देता है, जिससे subscribers लूप में बार-बार reconnect होते हैं और इस अंतराल के दौरान भेजे गए संदेश छूट जाते हैं।

उपयोगकर्ता बनाएँ और टॉपिक्स को लॉक करें

Authentication चालू है और अभी किसी के पास किसी भी चीज़ का एक्सेस नहीं है, यही इसका उद्देश्य है। अपने लिए एक admin अकाउंट और स्क्रिप्ट्स के लिए एक मशीन अकाउंट बनाएँ। ये कमांड्स कंटेनर के अंदर से /etc/ntfy/server.yml को पढ़ती हैं, इसीलिए कॉन्फ़िगरेशन फ़ाइल को वॉल्यूम माउंट किया गया है।

sudo docker compose exec ntfy ntfy user add --role=admin admin
sudo docker compose exec ntfy ntfy user add robot
sudo docker compose exec ntfy ntfy user list

प्रत्येक कमांड पासवर्ड के लिए प्रॉम्प्ट करती है। एक admin एक्सेस लिस्ट को अनदेखा करता है और हर टॉपिक को पढ़ और लिख सकता है, इसलिए उस अकाउंट को अपने और फोन ऐप के लिए रखें। robot एक साधारण उपयोगकर्ता है जिसके पास तब तक कोई एक्सेस नहीं होता जब तक आप उसे प्रदान नहीं करते।

sudo docker compose exec ntfy ntfy access robot alerts write
sudo docker compose exec ntfy ntfy access robot "alerts_*" write
sudo docker compose exec ntfy ntfy access

एक ACL (एक्सेस कंट्रोल लिस्ट) एंट्री में एक उपयोगकर्ता, एक टॉपिक और एक अनुमति होती है। टॉपिक या तो एक literal नाम होता है या एक पैटर्न जहाँ * किसी भी चीज़ से मेल खाता है, इसलिए alerts_* में alerts_backup और alerts_db दोनों शामिल हो जाते हैं, जिससे प्रति होस्ट अलग कमांड की आवश्यकता नहीं पड़ती। अनुमति write का अर्थ है केवल पब्लिश करना, ताकि यदि किसी क्रॉन जॉब से टोकन चोरी हो जाए, तो वह सब्सक्राइब करके डेटा पढ़ न सके। विशेष उपयोगकर्ता नाम everyone यह निर्धारित करता है कि एक अनधिकृत विज़िटर क्या कर सकता है, और आप इसका उपयोग केवल तब करेंगे जब आपको कुछ जानबूझकर सार्वजनिक करना हो, जैसे कि ntfy access everyone status read

स्क्रिप्ट्स में आपका पासवर्ड नहीं, बल्कि एक टोकन होना चाहिए।

sudo docker compose exec ntfy ntfy token add robot

यह कमांड tk_ से शुरू होने वाला एक टोकन प्रिंट करती है। एक टोकन को ठीक उसी उपयोगकर्ता का एक्सेस मिलता है जिससे वह संबंधित है, इसलिए यह टोकन केवल alerts टॉपिक्स पर पब्लिश कर सकता है और कुछ नहीं। ntfy token list यह दिखाता है कि क्या मौजूद है, और ntfy token remove उपयोगकर्ता के पासवर्ड को छुए बिना किसी टोकन को रद्द कर देता है।

अपना पहला संदेश भेजें और सुनिश्चित करें कि लॉक काम कर रहा है

सबसे पहले जाँचें कि दरवाजा बंद है या नहीं।

curl -s -o /dev/null -w '%{http_code}\n' -d "hello" https://ntfy.example.com/alerts

यह 403 प्रिंट करता है, और 403 सही उत्तर है: auth-default-access: "deny-all" गुमनाम पब्लिश (anonymous publish) को अस्वीकार कर देता है। अब एक वास्तविक संदेश भेजें।

curl -H "Authorization: Bearer tk_REPLACE_WITH_YOUR_TOKEN" \
  -H "Title: Nightly backup finished" \
  -H "Priority: default" \
  -H "Tags: white_check_mark" \
  -d "42 GB copied in 11 minutes" \
  https://ntfy.example.com/alerts

सर्वर JSON के रूप में संग्रहीत संदेश के साथ उत्तर देता है, जिससे आपको पता चलता है कि संदेश स्वीकार कर लिया गया है, न कि उसे अनदेखा किया गया है। Title पहली बोल्ड लाइन है। Priority 1 से 5 तक, या min से urgent तक नाम द्वारा चलता है, और यह तय करता है कि फोन ध्वनि करेगा या नहीं। जब नाम किसी ज्ञात इमोजी शॉर्ट कोड से मेल खाता है, तो Tags नोटिफिकेशन पर इमोजी बन जाते हैं, और मेल न खाने पर वे सादे टेक्स्ट के रूप में रहते हैं।

टर्मिनल से किसी टॉपिक को मॉनिटर करने के लिए, उसे स्ट्रीम करें:

curl -s -u admin https://ntfy.example.com/alerts/raw

curl पासवर्ड के लिए प्रॉम्प्ट करता है। प्रत्येक संदेश एक लाइन के रूप में आता है, और बीच-बीच में दिखाई देने वाली खाली लाइनें कीप-अलाइव (keepalives) हैं। ब्राउज़र में https://ntfy.example.com खोलकर और उसी अकाउंट से साइन इन करने पर आपको उसी स्ट्रीम का वेब ऐप संस्करण मिल जाता है।

रेट लिमिट सेट करें ताकि कोई एक स्क्रिप्ट सर्वर को फ्लड न कर सके

डिफ़ॉल्ट रूप से प्रत्येक विज़िटर को 60 रिक्वेस्ट की एक बकेट मिलती है, जो हर 5 सेकंड में एक रिक्वेस्ट की दर से रिफिल होती है। एक प्राइवेट सर्वर के लिए यह काफी उदार है, और रिट्राई लूप में फंसी कोई भी स्क्रिप्ट इसे पूरी तरह खत्म कर सकती है। server.yml में लिमिट्स जोड़ें।

visitor-request-limit-burst: 30
visitor-request-limit-replenish: "10s"
visitor-message-daily-limit: 500
sudo docker compose restart ntfy

लिमिट से अधिक रिक्वेस्ट भेजने वाले विज़िटर को डिलीवर किए गए मैसेज के बजाय HTTP 429 मिलता है। यह लिमिट प्रत्येक विज़िटर एड्रेस के आधार पर गिनी जाती है, यही कारण है कि behind-proxy: true इतना महत्वपूर्ण है: इसके बिना ntfy को केवल Caddy का एड्रेस दिखाई देता है, जिससे हर क्लाइंट एक ही विज़िटर के रूप में गिना जाता है, और एक शोर मचाने वाली स्क्रिप्ट उस बकेट को खत्म कर देती है जिसे आपका फोन और आपके अन्य सर्वर साझा करते हैं।

Cron job विफल होने पर अलर्ट

कमांड लाइन में टोकन न रखें। ps aux बॉक्स पर मौजूद हर यूजर को हर चल रही प्रोसेस की पूरी कमांड लाइन दिखाता है, इसलिए -H के साथ पास किया गया टोकन तब तक किसी भी लोकल अकाउंट द्वारा पढ़ा जा सकता है जब तक curl चलता है। एक curl कॉन्फ़िगरेशन फ़ाइल इससे बचाती है।

sudo install -d -m 700 /etc/ntfy-alert
printf 'header = "Authorization: Bearer tk_REPLACE_WITH_YOUR_TOKEN"\n' | sudo tee /etc/ntfy-alert/curlrc
sudo chmod 600 /etc/ntfy-alert/curlrc

अब जॉब को रैप करें। इसे /usr/local/bin/backup-with-alert.sh के रूप में सेव करें और इसे chmod 750 करें।

#!/bin/bash
out=$(/usr/local/bin/backup.sh 2>&1)
code=$?
if [ "$code" -ne 0 ]; then
  printf '%s' "$out" | tail -c 1000 | curl -K /etc/ntfy-alert/curlrc \
    -H "Title: backup.sh failed with exit $code" \
    -H "Priority: high" \
    -H "Tags: warning" \
    --data-binary @- \
    https://ntfy.example.com/alerts
fi
exit "$code"
17 3 * * * /usr/local/bin/backup-with-alert.sh >> /var/log/backup-alert.log 2>&1

$? को कमांड के ठीक बाद वाली लाइन पर कैप्चर किया जाता है, क्योंकि अगली चलने वाली कमांड इसे ओवरराइट कर देगी। आउटपुट tail -c 1000 के माध्यम से जाता है क्योंकि ntfy अधिकतम मैसेज साइज़ लागू करता है और नोटिफिकेशन कोई लॉग व्यूअर नहीं है। क्लोजिंग exit "$code" मूल स्टेटस को सुरक्षित रखता है, ताकि इस जॉब को मॉनिटर करने वाली कोई भी अन्य चीज़ अभी भी विफलता देख सके। एक रन के लिए स्क्रिप्ट को /bin/false पर पॉइंट करके पूरी प्रक्रिया का परीक्षण करें।

एक विफलता शाखा (failure branch) जो कभी निष्पादित नहीं होती, वह अलर्ट न होने से भी बदतर है, क्योंकि ऐसा लगता है कि चुप्पी का मतलब सफलता है। Cron आपके जॉब को लगभग खाली एनवायरनमेंट और आपके लॉगिन शेल की तुलना में बहुत छोटा PATH देता है, इसलिए जो स्क्रिप्ट आपके द्वारा मैन्युअल रूप से चलाने पर काम करती है, वह curl लाइन तक पहुँचने से पहले ही विफल हो सकती है। Cron जॉब क्यों नहीं चलती, इस पर गाइड उन एनवायरनमेंट ट्रैप्स को कवर करती है। हर जगह एब्सोल्यूट पाथ का उपयोग करें, और यह मानने के बजाय कि सब ठीक है, पहले शेड्यूल किए गए रन के बाद लॉग फ़ाइल को पढ़ें।

systemd unit विफल होने पर अलर्ट प्राप्त करना

Cron निर्धारित कार्यों के लिए उपयुक्त है। लंबे समय तक चलने वाली सेवाओं के लिए OnFailure= की आवश्यकता होती है, जिसे systemd तब चलाता है जब कोई unit failed स्थिति में आ जाती है। एक template unit बनाएँ और उसे सर्वर की प्रत्येक सेवा के लिए पुनः उपयोग करें। इसे /etc/systemd/system/ntfy-unit-failed@.service के रूप में सहेजें।

[Unit]
Description=Send an ntfy alert because %i failed

[Service]
Type=oneshot
ExecStart=/usr/local/bin/ntfy-unit-failed %i

फिर /usr/local/bin/ntfy-unit-failed करें, mode 750 के साथ:

#!/bin/bash
unit="$1"
journalctl -u "$unit" -n 15 --no-pager -o cat | tail -c 1000 | curl -K /etc/ntfy-alert/curlrc \
  -H "Title: $unit failed on $(hostname -s)" \
  -H "Priority: urgent" \
  -H "Tags: rotating_light" \
  --data-binary @- \
  https://ntfy.example.com/alerts

इसे एक drop-in के माध्यम से किसी सेवा के साथ जोड़ें, ताकि package upgrade आपके द्वारा किए गए बदलावों को ओवरराइट न कर सके।

sudo systemctl edit myapp.service
[Unit]
OnFailure=ntfy-unit-failed@%n.service

%n का विस्तार पूर्ण unit नाम में हो जाता है, इसलिए instance ntfy-unit-failed@myapp.service बन जाता है, और template के भीतर %i, myapp.service को script के पहले argument के रूप में भेजता है। यही वह तरीका है जिससे एक template हर unit के लिए काम कर सकता है। इसे एक ऐसी unit के साथ प्रमाणित करें जो जानबूझकर विफल हो जाती है, जिसे /etc/systemd/system/ntfy-selftest.service के रूप में सहेजें।

[Unit]
Description=Deliberately failing unit
OnFailure=ntfy-unit-failed@%n.service

[Service]
Type=oneshot
ExecStart=/bin/false
sudo systemctl daemon-reload
sudo systemctl start ntfy-selftest.service

start कमांड non-zero exit देती है और Job for ntfy-selftest.service failed because the control process exited with error code प्रिंट करती है, और लगभग एक सेकंड बाद फोन पर सूचना आ जानी चाहिए। बाद में test unit को हटा दें।

एक समस्या पर ध्यान देना आवश्यक है। OnFailure= केवल तब चलता है जब कोई unit failed स्थिति तक पहुँचती है, और Restart=always वाली सेवा शायद कभी उस स्थिति तक न पहुँचे, क्योंकि systemd उसे बार-बार restart करता रहता है। unit केवल तब विफल मानी जाती है जब वह StartLimitIntervalSec के भीतर StartLimitBurst बार restart होने की सीमा पार कर लेती है। जिन सेवाओं के बारे में आप सूचना प्राप्त करना चाहते हैं, उन पर ये दो मान सेट करें, अन्यथा crash loop दिनों तक चुपचाप चलती रहेगी। Timers ऊपर दिए गए cron पैटर्न का एक बेहतर विकल्प हैं, क्योंकि timer की service unit को OnFailure= स्वतः प्राप्त हो जाता है, और VPS पर systemd services और timers के लिए गाइड एक को convert करने की प्रक्रिया विस्तार से समझाती है।

Uptime monitor को उसी topic से जोड़ें

Uptime Kuma, the self-hosted status monitor, में ntfy notification type पहले से मौजूद है। Settings खोलें, फिर Notifications पर जाएँ, Setup Notification चुनें, Ntfy को चुनें, server URL को https://ntfy.example.com पर सेट करें और topic को alerts पर रखें, एक priority चुनें, और robot access token को paste करें। Save करने से पहले test notification भेजें, क्योंकि गलत topic name होने पर write grant उसे बिना किसी error के silent कर देता है।

इस व्यवस्था की एक वास्तविक सीमा यह है: यदि monitor उसी VPS पर चल रहा है, तो वह आपको यह नहीं बता पाएगा कि VPS down है, और यदि ntfy down है तो ntfy यह सूचना नहीं दे पाएगा। monitor को किसी दूसरी machine पर चलाएँ, और उस monitor के लिए एक दूसरा notification channel, जैसे email, रखें जो स्वयं ntfy की निगरानी करे। Uptime Kuma का Push monitor type एक अन्य blind spot को कवर करता है: आपका cron job सफल run के बाद एक push URL को call करता है, और जब ये calls आनी बंद हो जाती हैं तो Kuma alert भेजता है। failure branch केवल तब सक्रिय होती है जब job run होता है, इसलिए यह उस job के बारे में कुछ नहीं बताता जो कभी शुरू ही नहीं हुआ।

क्या self-hosted ntfy Android और iPhone पर काम करता है?

Android पर, हाँ, बिना किसी शर्त के। Google Play या F-Droid से ऐप इंस्टॉल करें, Settings खोलें, default server को https://ntfy.example.com पर सेट करें, user management स्क्रीन के अंतर्गत अपना account जोड़ें, और फिर alerts को subscribe करें। Instant delivery के लिए एक foreground service चलती रहती है ताकि फोन के doze mode में होने पर भी संदेश प्राप्त हो सकें, और इसके साथ आने वाला स्थायी notification Android की foreground services की आवश्यकता है, न कि कोई बग। F-Droid बिल्ड में कोई भी Firebase कोड नहीं होता है, इसलिए प्रत्येक subscription instant delivery का उपयोग करता है। ntfy एक UnifiedPush distributor के रूप में भी कार्य कर सकता है, जो Google की push service का एक खुला विकल्प है, इसलिए UnifiedPush का समर्थन करने वाले अन्य ऐप्स भी आपके सर्वर के माध्यम से संदेश भेज सकते हैं।

iOS पर, यह एक ऐसी निर्भरता के साथ काम करता है जिसे आप हटा नहीं सकते। Apple किसी backgrounded ऐप को केवल APNs (Apple push notification service) के माध्यम से ही जगाता है, और केवल वही पक्ष जिसके पास ऐप के signing credentials हैं, उसे संदेश भेज सकता है, इसलिए आपके सर्वर के पास ऐप तक सीधे पहुँचने का कोई तरीका नहीं है। ntfy इसे एक relay के साथ हल करता है: आपका सर्वर संदेश ID युक्त एक poll_request ntfy.sh को भेजता है, जो इसे Firebase और APNs के माध्यम से आगे भेजकर ऐप को जगाता है, और फिर ऐप आपके सर्वर से संदेश का मुख्य भाग प्राप्त करता है।

upstream-base-url: "https://ntfy.sh"

इसकी लागत के बारे में स्पष्ट रहें। संदेश की सामग्री आपके बॉक्स पर ही रहती है, लेकिन यह तथ्य कि एक संदेश आया है, और उसकी ID, उस बुनियादी ढांचे से होकर गुजरती है जिसे आप नहीं चलाते हैं। इस सेटिंग के बिना, self-hosted सर्वर से iPhone पर सूचनाएं देर से या बिल्कुल नहीं आती हैं, क्योंकि ऐप को जगाने वाला कोई नहीं होता है। relay को हटाने का एकमात्र तरीका यह है कि आप अपने स्वयं के Apple developer account और अपनी APNs कुंजियों के साथ iOS ऐप को स्वयं बनाएं और वितरित करें, जिसका अर्थ है वार्षिक शुल्क और प्रत्येक अपडेट के लिए एक नया बिल्ड। यदि relay आपके उपयोग के लिए अस्वीकार्य है, तो Android या desktop web app पर अलर्टिंग का उपयोग करें।

Backups, upgrades and pinning the image

दो paths को दोबारा generate नहीं किया जा सकता: /etc/ntfy/server.yml और /var/lib/ntfy/user.db। दूसरे path में सभी users, password hashes, ACL entries और tokens मौजूद होते हैं, इसलिए इसे private key की तरह सुरक्षित रखें।

sudo tar czf ntfy-backup.tgz -C / etc/ntfy var/lib/ntfy
sudo chmod 600 ntfy-backup.tgz

उस file को server से बाहर copy कर लें। cache.db में केवल हाल के messages होते हैं, जो ऊपर दिए गए cache-duration के साथ 12 घंटे तक के होते हैं, इसलिए इसे खोने से कोई नुकसान नहीं होता। Upgrading का अर्थ है compose file में tag को edit करना और pull करना।

sudo docker compose pull
sudo docker compose up -d
curl -s https://ntfy.example.com/v1/health

सबसे पहले release notes पढ़ें। SQLite databases start होने पर migrate हो जाती हैं, इसलिए schema change के बाद पुराने tag पर वापस जाना सुरक्षित नहीं है। जब तक नया version एक दिन तक न चल जाए, तब तक अपना लिया हुआ backup सुरक्षित रखें। Box पर मौजूद हर service के पास ऐसे paths की अपनी एक छोटी सूची होती है जिन्हें दोबारा generate नहीं किया जा सकता, और PhotoPrism और Immich की तुलना उसी प्रकार के VPS पर photo library के लिए उस सूची और उससे संबंधित backup commands को स्पष्ट करती है।

Gotify और Apprise

Gotify एक छोटा विकल्प है: इसमें एक वेब UI और एक Android ऐप के साथ केवल एक binary होती है। इसमें topic wildcards नहीं होते और न ही कोई आधिकारिक iOS client है, जो इसे उन private सर्वर के लिए उपयुक्त बनाता है जहाँ केवल Android का उपयोग किया जाता है। Apprise एक सर्वर के बजाय एक Python library और command line tool है। यह एक ही संदेश को ntfy सहित सौ से अधिक सेवाओं तक पहुँचा सकता है, जो उन scripts के लिए उपयुक्त है जिन्हें एक साथ कई स्थानों पर सूचना भेजनी होती है। ntfy वह विकल्प है जो आपको एक सर्वर, एक HTTP API और दोनों मोबाइल प्लेटफॉर्म पर ऐप्स प्रदान करता है। यही कारण है कि किराए के सर्वर से अलर्ट भेजने के लिए इसे सबसे अधिक उपयोग किया जाता है।

FAQ

Why does publishing to my ntfy server return 403?

auth-default-access: "deny-all" को server.yml में सेट करने पर, anonymous publish को अस्वीकार कर दिया जाता है, और यह अपेक्षित व्यवहार है। -u user:pass या -H "Authorization: Bearer tk_..." के साथ credentials भेजें। यदि आप पहले से ही एक token भेज रहे हैं और फिर भी 403 प्राप्त हो रहा है, तो उस token के पीछे के user के पास topic के लिए कोई matching ACL entry नहीं है। पूरी सूची देखने के लिए ntfy access चलाएँ। याद रखें कि write grant subscribing की अनुमति नहीं देता है, इसलिए एक account जो सफलतापूर्वक publish कर सकता है, उसे उसी topic को पढ़ने का प्रयास करने पर भी मना कर दिया जाएगा।

Do notifications work on iPhone with a self-hosted ntfy server?

ये एक relay के माध्यम से काम करते हैं जिसे आप टाल नहीं सकते। Apple केवल APNs (Apple push notification service) के माध्यम से ही apps को जगाता है, और केवल app का publisher ही इसे संदेश भेज सकता है, इसलिए ntfy संदेश ID युक्त एक poll_request को ntfy.sh पर भेजता है, जो इसे device तक relay करता है। server.yml में upstream-base-url: "https://ntfy.sh" सेट करें और container को restart करें। संदेश का मुख्य भाग अभी भी आपके सर्वर से ही fetch किया जाता है। इस setting के बिना, iOS notifications में देरी होती है या वे कभी नहीं दिखाई देते हैं।

Why did my cron job's ntfy alert never arrive?

यह साबित करने के लिए कि token और topic सही हैं, पहले curl line को अकेले चलाकर देखें। यदि यह manually काम करता है लेकिन cron से नहीं, तो विफलता alert से पहले की है: cron jobs को एक minimal environment और एक छोटे PATH के साथ चलाता है, इसलिए जो script किसी command को केवल नाम से call करती है, वह curl line तक पहुँचने से पहले ही बंद हो सकती है। Absolute paths का उपयोग करें, job के output को एक log file में redirect करें, और अगली बार चलने के बाद उस file को पढ़ें। delivery के बजाय 429 response का मतलब है कि rate limit काम कर रही है और आपकी script बहुत तेजी से retry कर रही है।

Should I expose ntfy on the public internet?

Phone apps को mobile networks से सर्वर तक पहुँचने की आवश्यकता होती है, इसलिए auth-default-access: "deny-all" और per-topic ACLs के साथ एक public HTTPS endpoint सामान्य setup है, और यह तब तक सुरक्षित है जब तक कोई भी topic everyone द्वारा पढ़ने योग्य न हो। यदि सभी subscribers आपके नियंत्रित machines हैं, तो केवल VPN-आधारित instance रखना उचित है। यह phones के लिए एक खराब विकल्प है, क्योंकि app केवल तभी संदेश प्राप्त करता है जब tunnel active हो, इसलिए alerts तब तक queue में रहते हैं जब तक phone फिर से connect न हो जाए।