VPS پر ntfy self-host کر کے server alerts بھیجیں
Docker Compose کے ساتھ VPS پر ntfy چلائیں، TLS فعال کریں، users اور ACLs سے topics محفوظ کریں، پھر cron اور systemd OnFailure سے alerts بھیجیں۔
self-hosted ntfy سرور کیا کرتا ہے
self-hosted ntfy سرور HTTP POST کو آپ کے فون پر push notification میں تبدیل کرتا ہے۔ آپ curl کے ذریعے پیغام publish کرتے ہیں، اور یہ Android app، iOS app، browser tab، یا کسی بھی ایسی چیز تک پہنچ جاتا ہے جو HTTP connection کو کھلا رکھ سکتی ہو۔ کسی client library کو install کرنے یا message broker چلانے کی ضرورت نہیں ہوتی۔
ntfy پیغامات کو topic کے ذریعے address کرتا ہے۔ topic URL path میں موجود ایک نام ہوتا ہے، جیسے https://ntfy.example.com/alerts، اور کسی کے اس پر publish کرتے ہی یہ وجود میں آ جاتا ہے۔ default install میں جس شخص کو یہ نام معلوم ہو، وہ topic کو پڑھ بھی سکتا ہے اور اس پر لکھ بھی سکتا ہے۔ اسی لیے project کی اپنی documentation topic name کا موازنہ password سے کرتی ہے۔ یہ model public ntfy.sh service کے لیے مناسب ہے۔ لیکن آپ کے backup failures والے server کے لیے یہ مناسب نہیں۔ اسی لیے یہ guide پہلا message بھیجے جانے سے پہلے 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 statusdig سے آپ کے سرور کا IP پرنٹ ہونا چاہیے۔ اگر یہ کچھ پرنٹ نہ کرے تو certificate issuance ناکام ہو جاتی ہے، کیونکہ certificate authority باہر سے اس نام کی جانچ کرتی ہے۔ Port 80 کھلا رہنا چاہیے، کیونکہ Let's Encrypt کے پیچھے استعمال ہونے والا پروٹوکول ACME (automatic certificate management environment) اسے HTTP challenge کے لیے استعمال کرتا ہے۔ ntfy container کو خود کوئی public port نہیں ملتا۔
ntfy کی config file لکھیں
Docker image میں config file شامل نہیں ہوتی، اس لیے آپ اسے خود بنائیں گے۔ اس guide میں بعد کے ہر command کو اسی file سے configuration ملے گی۔ پہلے وہ 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.ymlbase-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ان lines میں سے چار بنیادی اہمیت رکھتی ہیں۔ base-url عین public HTTPS address ہونا چاہیے، کیونکہ ntfy attachment links اور web app کی اپنی requests اسی address سے بناتا ہے۔ غلط value کی صورت میں web app load تو ہو جائے گی، لیکن ہر action ناکام ہو جائے گا۔ listen-http: ":2586" container کے اندر تمام interfaces پر bind کرتا ہے۔ یہ بظاہر غیر محتاط configuration ہے، لیکن درست ہے: container کا اپنا network namespace ہوتا ہے۔ اس لیے وہاں 127.0.0.1 پر bind کرنے سے port host سے ناقابل رسائی ہو جائے گا، اور Docker کا published port کبھی connect نہیں کر سکے گا۔ auth-default-access: "deny-all" پورا security posture متعین کرتا ہے، کیونکہ یہ explicit grant کے بغیر کسی کو read یا write کی اجازت نہیں دیتا۔ behind-proxy: true ntfy کو بتاتا ہے کہ client address X-Forwarded-For header سے حاصل کرے۔ اس طرح rate limits حقیقی visitors کو شمار کرتی ہیں، نہ کہ reverse proxy کو ایک بہت مصروف client سمجھتی ہیں۔
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.ymlDocker 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-stoppedcd /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 سے رسائی ممکن ہے۔ اس کے بجائے 2586:2586 لکھنے پر Docker اپنے firewall rules آپ کے rules سے پہلے شامل کر دیتا ہے۔ اس کا مطلب ہے کہ ufw status کے مطابق port بند ہونے کے باوجود وہ internet سے جواب دیتا ہے۔
اگر curl Connection refused دکھائے تو container log پڑھیں۔ /var/lib/ntfy/user.db پر permission error کا مطلب ہے کہ user: line ان directories کے owner سے match نہیں کرتی۔ اس لیے process اپنی database نہیں بنا سکتا اور exit ہو جاتا ہے۔ VPS کے لیے Docker Compose کی بنیادی باتوں کی guide میں volume ownership اور restart policies کی مزید تفصیل دی گئی ہے۔
Caddy کے ذریعے TLS سامنے رکھیں
Caddy خود certificate کی درخواست اور renewal کرتا ہے۔ اس طرح working 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 کے مواد کو تین سطروں سے replace کریں۔
ntfy.example.com {
reverse_proxy 127.0.0.1:2586
}sudo systemctl reload caddy
curl -s https://ntfy.example.com/v1/healthوہی {"healthy":true} جب HTTPS کے ذریعے چلتا ہے تو پورا path درست کام کرتا ہے۔ Caddy کی طرف سے 502 کا مطلب ہے کہ ntfy listening نہیں کر رہا۔ sudo ss -lntp | grep 2586 سے چیک کریں۔ Certificate error عموماً اس بات کی نشاندہی کرتا ہے کہ DNS record غلط ہے یا port 80 blocked ہے، اور sudo journalctl -u caddy -n 50 بتاتا ہے کہ ان میں سے کون سا مسئلہ ہے۔
اگر آپ پہلے سے nginx چلا رہے ہیں تو ntfy کی دستاویز کردہ proxy settings نقل کریں: proxy_http_version 1.1، proxy_buffering off، proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for، اور read اور send timeouts کم از کم تین minutes کے رکھیں۔ Subscriber اتنی دیر تک ایک HTTP connection کھلا رکھتا ہے جتنی دیر وہ listening کرتا ہے۔ nginx default طور پر 60 seconds بعد idle upstream connection بند کر دیتا ہے۔ اس کے نتیجے میں subscribers بار بار reconnect کرتے ہیں اور اس وقفے کے دوران بھیجے گئے messages ضائع ہو جاتے ہیں۔
صارف بنائیں اور topics تک رسائی محدود کریں
Authentication فعال ہے، لیکن ابھی کسی کو کسی چیز تک رسائی حاصل نہیں۔ یہی مطلوب ہے۔ اپنے لیے ایک admin account اور scripts کے لیے ایک machine account بنائیں۔ یہ commands container کے اندر سے /etc/ntfy/server.yml پڑھتی ہیں، اسی لیے config file کو volume mount کیا گیا ہے۔
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ہر command password طلب کرتی ہے۔ admin access list کو نظرانداز کرتا ہے اور ہر topic کو پڑھ اور لکھ سکتا ہے، اس لیے یہ account صرف اپنے اور phone app کے لیے رکھیں۔ robot ایک عام user ہے جسے اس وقت تک کسی چیز تک رسائی حاصل نہیں ہوتی جب تک آپ اسے کچھ اجازتیں نہ دیں۔
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 accessACL (access control list) entry میں user، topic اور permission شامل ہوتے ہیں۔ topic یا تو literal name ہوتا ہے یا pattern، جس میں * کسی بھی چیز سے match کرتا ہے۔ اس لیے alerts_*، alerts_backup اور alerts_db کا احاطہ کرتا ہے، اور ہر host کے لیے الگ command چلانے کی ضرورت نہیں رہتی۔ permission write کا مطلب صرف publish ہے، اس لیے cron job سے چوری کیا گیا token اپنے بھیجے ہوئے data کو subscribe کرکے دوبارہ نہیں پڑھ سکتا۔ مخصوص username everyone یہ طے کرتا ہے کہ unauthenticated visitor کیا کر سکتا ہے۔ اسے صرف کسی چیز کو دانستہ طور پر public بنانے کے لیے استعمال کریں، مثلاً ntfy access everyone status read۔
Scripts میں اپنا password رکھنے کے بجائے token رکھیں۔
sudo docker compose exec ntfy ntfy token add robotیہ command tk_ سے شروع ہونے والا token دکھاتی ہے۔ token کو اسی user کی تمام permissions حاصل ہوتی ہیں جس سے وہ وابستہ ہو، اس لیے یہ token alerts topics پر publish کر سکتا ہے اور اس کے علاوہ کچھ نہیں کر سکتا۔ ntfy token list موجودہ entries دکھاتا ہے، جبکہ ntfy token remove user کا password تبدیل کیے بغیر ایک entry revoke کر دیتا ہے۔
اپنا پہلا پیغام بھیجیں اور ثابت کریں کہ lock کام کر رہا ہے
شروع میں تصدیق کریں کہ door بند ہے۔
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/alertsserver stored message کو JSON کے طور پر واپس بھیجتا ہے۔ اس سے معلوم ہوتا ہے کہ پیغام قبول کیا گیا، ضائع نہیں ہوا۔ Title پہلی نمایاں سطر ہے۔ Priority کی قدر 1 سے 5 تک ہو سکتی ہے، یا نام کے ذریعے min سے urgent تک دی جا سکتی ہے۔ یہ طے کرتی ہے کہ phone sound چلائے گا یا نہیں۔ جب نام کسی معروف emoji short code سے match ہو تو Tags notification میں emoji بن جاتے ہیں، ورنہ plain text رہتے ہیں۔
terminal سے کسی topic کو دیکھنے کے لیے اسے stream کریں:
curl -s -u admin https://ntfy.example.com/alerts/rawcurl password طلب کرتا ہے۔ ہر پیغام ایک سطر کے طور پر آتا ہے، اور وقفے وقفے سے ظاہر ہونے والی خالی سطریں keepalive messages ہوتی ہیں۔ browser میں https://ntfy.example.com کھول کر اسی account سے sign in کرنے پر اسی stream کا web app version مل جاتا ہے۔
حد مقرر کریں تاکہ کوئی ایک اسکرپٹ سرور کو درخواستوں سے نہ بھر دے
بطور ڈیفالٹ، ہر visitor کو 60 requests کا ایک bucket ملتا ہے، جو ہر 5 seconds میں 1 request کے حساب سے دوبارہ بھرتا ہے۔ نجی سرور کے لیے یہ حد کافی فراخ ہے، اور retry loop میں پھنسا ہوا اسکرپٹ اسے مکمل طور پر استعمال کر دے گا۔ server.yml میں limits شامل کریں۔
visitor-request-limit-burst: 30
visitor-request-limit-replenish: "10s"
visitor-message-daily-limit: 500sudo docker compose restart ntfyحد سے تجاوز کرنے والے visitor کو پیغام پہنچانے کے بجائے HTTP 429 موصول ہوتا ہے۔ حد ہر visitor address کے حساب سے شمار ہوتی ہے۔ اسی لیے behind-proxy: true اتنا اہم ہے: اس کے بغیر ntfy کو صرف Caddy کا address نظر آتا ہے، ہر client ایک ہی visitor شمار ہوتا ہے، اور ایک شور مچانے والا اسکرپٹ وہ bucket ختم کر دیتا ہے جسے آپ کا phone اور دیگر servers بھی استعمال کرتے ہیں۔
کرون جاب کی ناکامی کی اطلاع
ٹوکن کو command line میں شامل نہ کریں۔ ps aux سسٹم پر چلنے والے ہر process کی مکمل command line ہر user کو دکھاتا ہے، اس لیے -H کے ذریعے بھیجا گیا ٹوکن curl کے چلنے تک کسی بھی local account کو نظر آ سکتا ہے۔ curl config file اس مسئلے سے بچاتی ہے۔
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اب job کو wrapper میں رکھیں۔ اسے /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$? کو command کے فوراً بعد والی line پر capture کیا جاتا ہے، کیونکہ اس کے بعد چلنے والی اگلی command اسے overwrite کر دے گی۔ output کو tail -c 1000 کے ذریعے بھیجا جاتا ہے، کیونکہ ntfy message size کی زیادہ سے زیادہ حد نافذ کرتا ہے اور notification log viewer نہیں ہوتی۔ اختتامی exit "$code" اصل status برقرار رکھتا ہے، اس لیے اس job کو monitor کرنے والا کوئی بھی دوسرا نظام اب بھی failure دیکھتا ہے۔ پوری ترتیب کی جانچ کے لیے ایک run میں script کو /bin/false کی طرف point کریں۔
ایسا failure branch جو کبھی execute ہی نہ ہو، alerting نہ ہونے سے بھی بدتر ہے، کیونکہ اس سے یہ غلط تاثر ملتا ہے کہ خاموشی کا مطلب success ہے۔ Cron آپ کے job کو تقریباً خالی environment اور login shell کے مقابلے میں کہیں مختصر PATH فراہم کرتا ہے، اس لیے جو script آپ ہاتھ سے چلانے پر کام کرتی ہے وہ curl line تک پہنچنے سے پہلے ہی fail ہو سکتی ہے۔ کرون جاب کے نہ چلنے کی وجوہات پر گائیڈ ان environment مسائل کی وضاحت کرتی ہے۔ ہر جگہ absolute paths استعمال کریں، اور پہلے scheduled run کے بعد log file پڑھیں؛ محض اندازہ نہ لگائیں۔
جب کوئی systemd unit fail ہو تو اطلاع دیں
Cron مقررہ کاموں کے لیے کافی ہے۔ طویل عرصے تک چلنے والی services کو OnFailure= درکار ہوتا ہے، جسے systemd اس وقت چلاتا ہے جب کوئی unit failed state میں داخل ہو۔ ایک template unit بنائیں اور اسے سرور کی ہر service کے لیے دوبارہ استعمال کریں۔ اسے /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 کے ذریعے کسی service کے ساتھ منسلک کریں، تاکہ package upgrade آپ کی ترمیم overwrite نہ کر سکے۔
sudo systemctl edit myapp.service[Unit]
OnFailure=ntfy-unit-failed@%n.service%n مکمل unit name میں تبدیل ہوتا ہے، اس لیے instance ntfy-unit-failed@myapp.service بن جاتا ہے، اور template کے اندر %i اسکرپٹ کو myapp.service اس کی پہلی argument کے طور پر دیتا ہے۔ اسی وجہ سے ایک template ہر unit کے لیے استعمال ہو سکتا ہے۔ اس کے درست کام کرنے کی تصدیق ایک ایسی unit سے کریں جو جان بوجھ کر fail ہو، اور اسے /etc/systemd/system/ntfy-selftest.service کے طور پر محفوظ کریں۔
[Unit]
Description=Deliberately failing unit
OnFailure=ntfy-unit-failed@%n.service
[Service]
Type=oneshot
ExecStart=/bin/falsesudo systemctl daemon-reload
sudo systemctl start ntfy-selftest.servicestart command non-zero کے ساتھ ختم ہوتا ہے اور Job for ntfy-selftest.service failed because the control process exited with error code دکھاتا ہے، جبکہ تقریباً ایک second بعد فون پر alert آ جانا چاہیے۔ اس کے بعد test unit حذف کریں۔
ایک اہم مسئلہ قابلِ توجہ ہے۔ OnFailure= صرف اس وقت چلتا ہے جب کوئی unit failed state تک پہنچے، اور Restart=always والی service شاید کبھی وہاں نہ پہنچے، کیونکہ systemd اسے مسلسل restart کرتا رہتا ہے۔ unit صرف اس وقت fail ہوتی ہے جب وہ StartLimitIntervalSec کے اندر StartLimitBurst restarts سے تجاوز کر جائے۔ جن services کے بارے میں اطلاع چاہیے، ان پر یہ دونوں values مقرر کریں، ورنہ crash loop کئی دن تک خاموشی سے چلتا رہے گا۔ اوپر بیان کردہ cron pattern کے لیے timers زیادہ صاف متبادل ہیں، کیونکہ timer کی service unit کو OnFailure= بلا معاوضہ مل جاتا ہے، اور VPS پر systemd services اور timers کی رہنمائی ایک کو تبدیل کرنے کا طریقہ بتاتی ہے۔
اسی topic میں uptime monitor کو مربوط کریں
Uptime Kuma، 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 پر خاموشی سے ناکام ہو جاتا ہے جو اس topic کا احاطہ نہیں کرتا۔
اس انتظام کی عملی حد واضح ہے: اسی VPS پر چلنے والا monitor یہ نہیں بتا سکتا کہ VPS down ہے، اور ntfy یہ اطلاع نہیں دے سکتا کہ ntfy down ہے۔ Monitor کو کسی مختلف machine پر چلائیں، اور اسے دوسرا notification channel، مثلاً email، دیں تاکہ وہ خود ntfy کو monitor کرنے والے monitor کے لیے بھی alert بھیج سکے۔ Uptime Kuma کا Push monitor type دوسری blind spot کو حل کرتا ہے: کامیاب run کے بعد آپ کا cron job 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 screen میں اپنا account شامل کریں، پھر alerts کو subscribe کریں۔ فوری delivery ایک foreground service چلاتی رہتی ہے، اس لیے فون کے doze mode میں ہونے کے باوجود messages پہنچ جاتے ہیں۔ اس کے ساتھ ظاہر ہونے والی مستقل notification، کسی bug کے بجائے، foreground services کے لیے Android کی ضرورت ہے۔ F-Droid build میں Firebase code بالکل شامل نہیں، اس لیے ہر subscription فوری delivery استعمال کرتی ہے۔ ntfy بطور UnifiedPush distributor بھی کام کر سکتا ہے۔ یہ Google کی push service کا open متبادل ہے، اس لیے UnifiedPush کو support کرنے والی دیگر ایپس بھی آپ کے server کے ذریعے delivery کر سکتی ہیں۔
iOS پر یہ ایک ایسی dependency کے ساتھ کام کرتا ہے جسے آپ ختم نہیں کر سکتے۔ Apple کسی backgrounded app کو صرف APNs (Apple push notification service) کے ذریعے جگاتا ہے، اور صرف وہ فریق اسے message بھیج سکتا ہے جس کے پاس app کی signing credentials ہوں۔ اس لیے آپ کے server کے پاس app تک براہِ راست پہنچنے کا کوئی طریقہ نہیں۔ ntfy اس مسئلے کو relay کے ذریعے حل کرتا ہے: آپ کا server message ID رکھنے والا poll_request ntfy.sh کو بھیجتا ہے، جو اسے Firebase اور APNs کے ذریعے آگے پہنچا کر app کو جگاتا ہے، اور app پھر message body آپ کے server سے حاصل کرتی ہے۔
upstream-base-url: "https://ntfy.sh"اس کی قیمت کو واضح طور پر سمجھیں۔ Message content آپ کے server پر رہتا ہے، لیکن message پہنچنے کی حقیقت اور اس کی ID ایسی infrastructure سے گزرتی ہے جسے آپ نہیں چلاتے۔ اس setting کے بغیر، self-hosted server سے iPhone پر notifications دیر سے پہنچتی ہیں یا بالکل نہیں پہنچتیں، کیونکہ app کو جگانے والا کوئی نظام نہیں ہوتا۔ Relay ختم کرنے کا واحد طریقہ یہ ہے کہ آپ اپنی Apple developer account اور اپنی APNs keys استعمال کرتے ہوئے iOS app خود build اور ship کریں۔ اس کے لیے سالانہ fee اور ہر update کے لیے دوبارہ build درکار ہوتی ہے۔ اگر relay آپ کے استعمال کے لیے قابل قبول نہیں، تو alerting کو Android یا desktop web app تک محدود رکھیں۔
Backups، upgrades اور image کو pin کرنا
دو paths دوبارہ generate نہیں کیے جا سکتے: /etc/ntfy/server.yml اور /var/lib/ntfy/user.db۔ دوسرے path میں ہر user، password hash، ACL entry اور token موجود ہوتا ہے، اس لیے اسے 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 گھنٹے کے messages، اس لیے اسے کھونے سے کوئی قابلِ تحفظ چیز ضائع نہیں ہوتی۔ Upgrade کرنے کے لیے compose file میں tag تبدیل کریں اور image 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 پر واپس جانا محفوظ نہیں ہے۔ جو backup آپ نے ابھی لیا ہے، اسے نئی version کے ایک دن تک چلنے تک محفوظ رکھیں۔
Gotify اور Apprise
Gotify نسبتاً چھوٹا اختیار ہے: اس میں ایک binary، web UI اور Android app شامل ہیں۔ اس میں topic wildcards نہیں ہیں اور official iOS client بھی نہیں ہے۔ یہ ایسی private box کے لیے موزوں ہے جہاں صرف Android کو target کرنا ہو۔ Apprise، server کے بجائے Python library اور command line tool ہے۔ یہ ایک ہی message کو ntfy سمیت 100 سے زیادہ services تک بھیج سکتا ہے۔ یہ ایسے script کے لیے موزوں ہے جسے بیک وقت کئی مقامات تک message پہنچانا ہو۔ ntfy آپ کو server، HTTP API اور دونوں mobile platforms کے لیے apps فراہم کرتا ہے۔ اسی لیے rented server سے alerting کے لیے عموماً ntfy منتخب کیا جاتا ہے۔
FAQ
میرے ntfy server پر publish کرنے سے 403 کیوں آتا ہے؟
auth-default-access: "deny-all" میں server.yml کے ساتھ anonymous publish مسترد ہو جاتا ہے، اور یہی مطلوبہ رویہ ہے۔ -u user:pass یا -H "Authorization: Bearer tk_..." کے ساتھ credentials بھیجیں۔ اگر آپ پہلے ہی token بھیج رہے ہیں اور پھر بھی 403 مل رہا ہے تو اس token کے پیچھے موجود user کے پاس topic کے لیے مطابقت رکھنے والی ACL entry نہیں ہے۔ مکمل فہرست دکھانے کے لیے ntfy access چلائیں۔ یاد رکھیں کہ write grant سے subscribe کرنے کی اجازت نہیں ملتی، اس لیے جو account کامیابی سے publish کرتا ہے اسے اسی topic کو پڑھنے کی کوشش پر پھر بھی مسترد کیا جائے گا۔
کیا self-hosted ntfy server کے ساتھ iPhone پر notifications کام کرتی ہیں؟
ہاں، لیکن ایک ایسے relay کے ذریعے جس سے گریز ممکن نہیں۔ Apple apps کو صرف APNs (Apple push notification service) کے ذریعے بیدار کرتا ہے، اور اسے صرف app کا publisher ہی پیغام بھیج سکتا ہے۔ اسی لیے ntfy ایک poll_request بھیجتا ہے، جس میں message ID شامل ہوتی ہے، ntfy.sh کو، اور ntfy.sh اسے device تک پہنچاتا ہے۔ upstream-base-url: "https://ntfy.sh" کو server.yml میں set کریں اور container restart کریں۔ اصل message body پھر بھی آپ کے server سے fetch ہوتی ہے۔ اس setting کے بغیر iOS notifications تاخیر سے آتی ہیں یا بالکل ظاہر نہیں ہوتیں۔
میرے cron job کا ntfy alert کبھی کیوں نہیں پہنچا؟
پہلے curl والی line کو الگ سے چلائیں تاکہ token اور topic کے درست ہونے کی تصدیق ہو جائے۔ اگر یہ دستی طور پر کام کرتی ہے لیکن cron سے نہیں، تو failure alert سے پہلے کے مرحلے میں ہے: cron jobs کو minimal environment اور مختصر PATH کے ساتھ چلاتا ہے، اس لیے bare name سے command چلانے والی script curl line تک پہنچنے سے پہلے ختم ہو سکتی ہے۔ Absolute paths استعمال کریں، job کا output log file میں redirect کریں، اور اگلی run کے بعد وہ file پڑھیں۔ Delivery کے بجائے 429 response ملنے کا مطلب ہے کہ rate limit کام کر رہی ہے اور آپ کی script بہت تیزی سے دوبارہ کوشش کر رہی ہے۔
کیا مجھے ntfy کو public internet پر expose کرنا چاہیے؟
Phone apps کو mobile networks سے server تک پہنچنا ہوتا ہے، اس لیے auth-default-access: "deny-all" اور per-topic ACLs کے ساتھ public HTTPS endpoint معمول کی configuration ہے، اور یہ اس وقت محفوظ ہے جب کوئی topic everyone کے لیے readable نہ ہو۔ VPN-only instance اس وقت مناسب ہے جب ہر subscriber ایسا machine ہو جسے آپ control کرتے ہوں۔ Phones کے لیے یہ کم موزوں ہے، کیونکہ app صرف tunnel کے فعال ہونے کے دوران notifications وصول کرتی ہے۔ Tunnel دوبارہ connect ہونے تک alerts queue میں رہتی ہیں۔