Własny serwer ntfy: konfiguracja powiadomień push
Dowiedz się, jak uruchomić ntfy w Docker Compose za własnym serwerem TLS. Zabezpiecz tematy za pomocą ACL oraz zintegruj powiadomienia z cron i systemd OnFailure w Linux.
Działanie własnego serwera ntfy
Własny serwer ntfy przekształca żądanie HTTP POST w powiadomienie push na telefonie. Publikacja odbywa się za pomocą curl, a wiadomość dociera do aplikacji na Androida, iOS, karty przeglądarki lub dowolnego innego klienta utrzymującego otwarte połączenie HTTP. Nie ma potrzeby instalowania bibliotek klienckich ani uruchamiania brokera wiadomości.
ntfy adresuje wiadomości za pomocą tematów (topics). Temat to nazwa w ścieżce URL, taka jak https://ntfy.example.com/alerts, która powstaje w momencie pierwszej publikacji. W domyślnej instalacji każdy, kto zna tę nazwę, może odczytywać i zapisywać wiadomości w temacie, dlatego dokumentacja projektu porównuje nazwę tematu do hasła. Ten model sprawdza się w publicznej usłudze ntfy.sh. Nie jest on jednak odpowiedni dla serwera przesyłającego powiadomienia o błędach kopii zapasowych, dlatego niniejszy przewodnik aktywuje uwierzytelnianie przed wysłaniem pierwszej wiadomości.
Wymagania wstępne
Wymagany jest serwer VPS z systemem Ubuntu 24.04 lub Debian 13, zainstalowanym Docker Engine oraz wtyczką Compose, nazwa domenowa oraz niewielka ilość pamięci RAM. Należy utworzyć rekord A w systemie DNS (domain name system), wskazujący ntfy.example.com na publiczny adres IP serwera, a następnie potwierdzić poprawność rozpoznawania nazwy przed wykonaniem jakichkolwiek dalszych czynności.
dig +short ntfy.example.com
sudo ufw allow 80,443/tcp
sudo ufw statusPolecenie dig musi zwrócić adres IP serwera. Wydawanie certyfikatu zakończy się niepowodzeniem, jeśli polecenie nie zwróci żadnej wartości, ponieważ urząd certyfikacji weryfikuje nazwę z zewnątrz. Port 80 musi pozostać otwarty, ponieważ protokół ACME (automatic certificate management environment), wykorzystywany przez Let's Encrypt, używa go do wyzwania HTTP. Sam kontener ntfy nie otrzymuje publicznego portu.
Tworzenie pliku konfiguracyjnego ntfy
Obraz Docker nie zawiera pliku konfiguracyjnego, dlatego należy utworzyć go samodzielnie. Każde polecenie w dalszej części tego przewodnika odwołuje się do tego pliku. Najpierw należy ustalić identyfikator użytkownika (UID) oraz identyfikator grupy (GID), z którymi będzie uruchamiany kontener.
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: falseCztery z tych linii mają kluczowe znaczenie. base-url musi być dokładnym publicznym adresem HTTPS, ponieważ ntfy buduje na jego podstawie linki do załączników oraz żądania samej aplikacji webowej; błędna wartość sprawi, że aplikacja załaduje się, ale każda akcja zakończy się niepowodzeniem. listen-http: ":2586" wiąże usługę ze wszystkimi interfejsami wewnątrz kontenera, co wygląda na niedbałość, ale jest poprawne: kontener posiada własną przestrzeń nazw sieciowych, więc powiązanie z 127.0.0.1 sprawiłoby, że port byłby nieosiągalny z poziomu hosta, a opublikowany port Docker nigdy by nie nawiązał połączenia. auth-default-access: "deny-all" stanowi podstawę bezpieczeństwa, ponieważ odmawia uprawnień do odczytu i zapisu każdemu, kto nie posiada wyraźnego zezwolenia. behind-proxy: true instruuje ntfy, aby pobierało adres klienta z nagłówka X-Forwarded-For, dzięki czemu limity zapytań (rate limits) zliczają rzeczywistych użytkowników, zamiast traktować reverse proxy jako jednego, bardzo aktywnego klienta.
enable-login: true umożliwia aplikacji webowej oraz aplikacjom mobilnym logowanie za pomocą hasła. enable-signup pozostaje ustawione na false, ponieważ samodzielne tworzenie kont na prywatnym serwerze stanowi otwartą furtkę z dodatkowymi utrudnieniami.
sudo chown "$(id -u):$(id -g)" /etc/ntfy/server.yml
sudo chmod 600 /etc/ntfy/server.ymlUruchamianie ntfy za pomocą Docker Compose
Umieść poniższą treść w /opt/ntfy/compose.yaml, zastępując 1000:1000 dwiema liczbami id -u oraz id -g wyświetlonymi powyżej.
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/healthPoprawnie działający serwer odpowiada {"healthy":true}. Dwa szczegóły w tym pliku compose są zamierzone. Obraz jest przypięty do wersji v2.27.0, czyli wydania aktualnego na sierpień 2026, zamiast do latest. Użycie latest spowodowałoby, że kolejne docker compose pull zmieniłoby wersję serwera, o czym dowiedziałbyś się dopiero z dziennika zmian. Port jest opublikowany jako 127.0.0.1:2586:2586, dzięki czemu kontener jest dostępny wyłącznie z adresu loopback hosta. Wpisanie 2586:2586 spowodowałoby, że Docker wstawi własne reguły firewalla przed Twoimi, co oznacza, że port odpowiadałby na zapytania z Internetu, mimo że ufw status wskazuje, iż port jest zamknięty.
Jeśli curl zwraca Connection refused, sprawdź logi kontenera. Błąd uprawnień w /var/lib/ntfy/user.db oznacza, że linia user: nie odpowiada właścicielowi tych katalogów, przez co proces nie może utworzyć własnej bazy danych i kończy działanie. Podstawowy przewodnik po Docker Compose dla VPS zawiera bardziej szczegółowe informacje na temat własności wolumenów i polityk restartu.
Wdrożenie TLS za pomocą Caddy
Caddy automatycznie żąda i odnawia certyfikaty, co stanowi najszybszą drogę do uzyskania działającego 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 caddyZastąp zawartość /etc/caddy/Caddyfile trzema liniami.
ntfy.example.com {
reverse_proxy 127.0.0.1:2586
}sudo systemctl reload caddy
curl -s https://ntfy.example.com/v1/healthTen sam {"healthy":true} przez HTTPS oznacza, że cała ścieżka działa poprawnie. 502 z Caddy oznacza, że ntfy nie nasłuchuje: sprawdź to za pomocą sudo ss -lntp | grep 2586. Błąd certyfikatu zazwyczaj oznacza błędny rekord DNS lub zablokowany port 80, a sudo journalctl -u caddy -n 50 wskazuje, który z nich jest przyczyną.
Jeśli używasz już nginx, skopiuj ustawienia proxy opisane w dokumentacji ntfy: proxy_http_version 1.1, proxy_buffering off, proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for oraz ustaw limity czasu odczytu i wysyłania na co najmniej trzy minuty. Subskrybent utrzymuje otwarte połączenie HTTP tak długo, jak długo nasłuchuje, a nginx domyślnie zamyka bezczynne połączenie upstream po 60 sekundach. Powoduje to, że subskrybenci łączą się ponownie w pętli, a wiadomości wysłane w trakcie przerwy są tracone.
Tworzenie użytkowników i ograniczanie dostępu do tematów
Uwierzytelnianie jest włączone, a nikt nie ma jeszcze dostępu do zasobów, co jest zamierzonym stanem. Należy utworzyć jedno konto administratora dla siebie oraz jedno konto maszynowe dla skryptów. Poniższe polecenia odczytują /etc/ntfy/server.yml z wnętrza kontenera, dlatego plik konfiguracyjny jest zamontowany jako wolumen.
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 listKażde z nich wymaga podania hasła. Administrator ignoruje listę kontroli dostępu i może odczytywać oraz zapisywać każdy temat, dlatego należy zachować to konto do użytku własnego oraz w aplikacji mobilnej. robot to zwykły użytkownik, który nie posiada żadnych uprawnień, dopóki nie zostaną one nadane.
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 accessWpis na liście kontroli dostępu (ACL) składa się z użytkownika, tematu oraz uprawnienia. Temat może być konkretną nazwą lub wzorcem, w którym * dopasowuje dowolny ciąg znaków, więc alerts_* obejmuje alerts_backup oraz alerts_db bez konieczności wydawania osobnego polecenia dla każdego hosta. Uprawnienie write oznacza wyłącznie możliwość publikowania, dzięki czemu token skradziony z zadania cron nie pozwoli na subskrypcję i odczyt wysłanych danych. Specjalna nazwa użytkownika everyone określa uprawnienia dla nieuwierzytelnionych gości; należy jej używać tylko w przypadku celowego udostępniania danych publicznie, na przykład ntfy access everyone status read.
Skrypty powinny korzystać z tokena, a nie z hasła użytkownika.
sudo docker compose exec ntfy ntfy token add robotPolecenie wyświetla token rozpoczynający się od tk_. Token dziedziczy dokładnie takie uprawnienia, jakie posiada użytkownik, do którego należy, więc w tym przypadku pozwala on jedynie na publikowanie w tematach alerts i nic więcej. ntfy token list wyświetla istniejące tokeny, a ntfy token remove unieważnia wybrany token bez zmiany hasła użytkownika.
Wysłanie pierwszej wiadomości i weryfikacja działania blokady
Rozpocznij od sprawdzenia, czy drzwi są zamknięte.
curl -s -o /dev/null -w '%{http_code}\n' -d "hello" https://ntfy.example.com/alertsPolecenie to wyświetla 403, a 403 jest poprawną odpowiedzią: auth-default-access: "deny-all" odrzuca anonimowe publikacje. Teraz wyślij właściwą wiadomość.
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/alertsSerwer odpowiada zapisaną wiadomością w formacie JSON, co potwierdza, że została ona zaakceptowana, a nie odrzucona. Title to pogrubiona pierwsza linia. Priority przyjmuje wartości od 1 do 5 lub nazwy od min do urgent i decyduje o tym, czy telefon wyemituje dźwięk. Tags zamieniają się w emoji w powiadomieniu, jeśli nazwa odpowiada znanemu krótkiemu kodowi emoji, w przeciwnym razie pozostają zwykłym tekstem.
Aby monitorować temat z poziomu terminala, przesyłaj go strumieniowo:
curl -s -u admin https://ntfy.example.com/alerts/rawcurl wyświetli monit o podanie hasła. Każda wiadomość dociera jako pojedyncza linia, a pojawiające się od czasu do czasu puste linie to sygnały podtrzymujące połączenie (keepalives). Otwarcie https://ntfy.example.com w przeglądarce i zalogowanie się na to samo konto zapewnia dostęp do wersji webowej tego samego strumienia.
Ustawienie limitów zapytań w celu ochrony serwera przed przeciążeniem przez skrypty
Domyślnie każdy użytkownik otrzymuje pulę 60 zapytań, uzupełnianą w tempie jednego zapytania co 5 sekund. Jest to wartość wystarczająca dla serwera prywatnego, jednak skrypt zablokowany w pętli ponowień może ją szybko wyczerpać. Należy dodać limity w server.yml.
visitor-request-limit-burst: 30
visitor-request-limit-replenish: "10s"
visitor-message-daily-limit: 500sudo docker compose restart ntfyUżytkownik przekraczający limit otrzymuje odpowiedź HTTP 429 zamiast żądanej treści. Limit jest liczony dla każdego adresu odwiedzającego, dlatego behind-proxy: true ma tak duże znaczenie: bez tego parametru ntfy widzi jedynie adres IP serwera Caddy. W efekcie każdy klient jest traktowany jako ten sam użytkownik, a jeden wadliwy skrypt wyczerpuje pulę wspólną dla telefonu oraz pozostałych serwerów.
Powiadomienie o błędzie zadania cron
Nie należy umieszczać tokena w wierszu poleceń. ps aux wyświetla pełny wiersz poleceń każdego uruchomionego procesu wszystkim użytkownikom w systemie, więc token przekazany za pomocą -H jest możliwy do odczytania przez dowolne konto lokalne przez cały czas działania curl. Plik konfiguracyjny curl pozwala uniknąć tego problemu.
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/curlrcTeraz należy przygotować skrypt zadania. Zapisz go jako /usr/local/bin/backup-with-alert.sh i nadaj mu uprawnienia 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$? jest przechwytywane w wierszu bezpośrednio po poleceniu, ponieważ kolejne uruchomione polecenie nadpisałoby tę wartość. Dane wyjściowe są przetwarzane przez tail -c 1000, ponieważ ntfy wymusza maksymalny rozmiar wiadomości, a powiadomienie nie służy do przeglądania logów. Zamykające exit "$code" zachowuje oryginalny kod wyjścia, dzięki czemu każdy inny proces monitorujący to zadanie nadal wykryje błąd. Przetestuj całość, kierując skrypt na /bin/false podczas jednego uruchomienia.
Gałąź obsługi błędów, która nigdy się nie wykonuje, jest gorsza niż brak powiadomień, ponieważ sprawia wrażenie, że brak informacji oznacza sukces. Cron zapewnia zadaniu niemal puste środowisko i znacznie krótszą zmienną PATH niż powłoka logowania, więc skrypt działający poprawnie przy ręcznym uruchomieniu może zakończyć się przed dotarciem do linii z curl. Przewodnik dotyczący przyczyn nieuruchamiania się zadań cron omawia te pułapki środowiskowe. Należy wszędzie stosować ścieżki bezwzględne i po pierwszym zaplanowanym uruchomieniu sprawdzić plik logu, zamiast zakładać poprawne działanie.
Powiadamianie o awarii jednostki systemd
Cron obsługuje zadania zaplanowane. Usługi działające w tle wymagają OnFailure=, które systemd uruchamia, gdy jednostka przechodzi w stan failed. Należy utworzyć jedną jednostkę szablonu i używać jej dla każdej usługi na serwerze. Zapisz ją jako /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 %iNastępnie /usr/local/bin/ntfy-unit-failed, z uprawnieniami 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/alertsPodepnij ją do usługi za pomocą pliku typu drop-in, aby aktualizacja pakietu nie nadpisała wprowadzonych zmian.
sudo systemctl edit myapp.service[Unit]
OnFailure=ntfy-unit-failed@%n.service%n rozwija się do pełnej nazwy jednostki, więc instancja staje się ntfy-unit-failed@myapp.service, a %i wewnątrz szablonu przekazuje myapp.service do skryptu jako pierwszy argument. Dzięki temu jeden szablon może obsługiwać każdą jednostkę. Sprawdź działanie za pomocą jednostki, która celowo kończy się błędem, zapisanej jako /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.servicePolecenie startowe kończy się kodem błędu i wypisuje Job for ntfy-selftest.service failed because the control process exited with error code, a powiadomienie powinno pojawić się około sekundę później. Po zakończeniu testu usuń jednostkę testową.
Należy zwrócić uwagę na jedną pułapkę. OnFailure= uruchamia się tylko wtedy, gdy jednostka osiągnie stan failed, a usługa z Restart=always może go nigdy nie osiągnąć, ponieważ systemd ciągle ją restartuje. Jednostka przechodzi w stan awarii dopiero po przekroczeniu StartLimitBurst restartów w czasie StartLimitIntervalSec. Ustaw te dwie wartości dla każdej usługi, o której chcesz otrzymywać powiadomienia, w przeciwnym razie pętla awarii może trwać niezauważenie przez wiele dni. Timery stanowią czystszy zamiennik dla powyższego wzorca cron, ponieważ jednostka usługi timera otrzymuje OnFailure= automatycznie, a przewodnik po usługach i timerach systemd na VPS opisuje proces konwersji.
Integracja monitora dostępności z tym samym tematem
Uptime Kuma, samodzielnie hostowany monitor statusu, posiada wbudowany typ powiadomień ntfy. Otwórz Settings, następnie Notifications, wybierz Setup Notification, wskaż Ntfy, ustaw adres URL serwera na https://ntfy.example.com, a temat na alerts. Wybierz priorytet i wklej token dostępu robot. Przed zapisaniem wyślij powiadomienie testowe, ponieważ błędna nazwa tematu powoduje cichą awarię w przypadku uprawnień write, które nie obejmują danego tematu.
Realne ograniczenie tego rozwiązania: monitor działający na tym samym VPS nie poinformuje o awarii tego VPS, a ntfy nie dostarczy informacji o awarii samego ntfy. Uruchom monitor na innej maszynie i skonfiguruj drugi kanał powiadomień, na przykład e-mail, dla monitora sprawdzającego ntfy. Typ monitora Push w Uptime Kuma rozwiązuje inny problem: zadanie cron wywołuje adres URL typu push po poprawnym zakończeniu pracy, a Kuma wysyła alert, gdy wywołania przestają docierać. Gałąź obsługi błędu uruchamia się tylko wtedy, gdy zadanie wystartuje, więc nie informuje o zadaniu, które w ogóle się nie rozpoczęło.
Czy samodzielnie hostowany ntfy działa na systemach Android i iPhone?
Na systemie Android działa bez ograniczeń. Należy zainstalować aplikację ze sklepu Google Play lub F-Droid, otworzyć ustawienia, ustawić domyślny serwer na https://ntfy.example.com, dodać konto w ekranie zarządzania użytkownikami, a następnie zasubskrybować alerts. Funkcja natychmiastowego dostarczania utrzymuje działającą usługę pierwszoplanową, dzięki czemu wiadomości docierają nawet w trybie uśpienia telefonu, a towarzyszące temu stałe powiadomienie jest wymogiem systemu Android dla usług pierwszoplanowych, a nie błędem. Wersja z F-Droid nie zawiera żadnego kodu Firebase, więc każda subskrypcja korzysta z natychmiastowego dostarczania. ntfy może również pełnić rolę dystrybutora UnifiedPush, otwartego zamiennika usługi powiadomień Google, dzięki czemu inne aplikacje wspierające UnifiedPush mogą również korzystać z Twojego serwera.
Na systemie iOS rozwiązanie działa z jedną zależnością, której nie można usunąć. Apple wybudza aplikację działającą w tle wyłącznie za pośrednictwem APNs (Apple push notification service), a tylko podmiot posiadający dane uwierzytelniające podpis aplikacji może wysyłać do niej powiadomienia, więc Twój serwer nie ma możliwości bezpośredniego połączenia z aplikacją. ntfy rozwiązuje ten problem za pomocą przekaźnika: Twój serwer wysyła poll_request zawierający identyfikator wiadomości do ntfy.sh, który przekazuje go przez Firebase i APNs w celu wybudzenia aplikacji, a następnie aplikacja pobiera treść wiadomości z Twojego serwera.
upstream-base-url: "https://ntfy.sh"Należy mieć świadomość kosztów tego rozwiązania. Treść wiadomości pozostaje na Twoim serwerze, ale fakt nadejścia wiadomości oraz jej identyfikator przechodzą przez infrastrukturę, której nie kontrolujesz. Bez tego ustawienia powiadomienia na iPhone z samodzielnie hostowanego serwera docierają z opóźnieniem lub wcale, ponieważ nic nie wybudza aplikacji. Jedynym sposobem na usunięcie przekaźnika jest samodzielne zbudowanie i opublikowanie aplikacji na iOS przy użyciu własnego konta deweloperskiego Apple oraz własnych kluczy APNs, co wiąże się z roczną opłatą oraz koniecznością przebudowy aplikacji przy każdej aktualizacji. Jeśli korzystanie z przekaźnika jest nieakceptowalne, należy pozostać przy powiadomieniach na systemie Android lub w desktopowej aplikacji webowej.
Kopie zapasowe, aktualizacje i przypinanie obrazu
Dwie ścieżki nie podlegają regeneracji: /etc/ntfy/server.yml oraz /var/lib/ntfy/user.db. Druga z nich przechowuje wszystkich użytkowników, skróty haseł, wpisy ACL oraz tokeny, dlatego należy traktować ją jak klucz prywatny.
sudo tar czf ntfy-backup.tgz -C / etc/ntfy var/lib/ntfy
sudo chmod 600 ntfy-backup.tgzSkopiuj ten plik poza serwer. cache.db przechowuje jedynie ostatnie wiadomości, obejmujące 12 godzin pracy przy ustawieniu cache-duration powyżej, więc jego utrata nie wiąże się z żadnym ryzykiem. Aktualizacja polega na edycji tagu w pliku compose i pobraniu obrazu.
sudo docker compose pull
sudo docker compose up -d
curl -s https://ntfy.example.com/v1/healthNajpierw zapoznaj się z informacjami o wydaniu. Bazy danych SQLite migrują przy starcie, dlatego powrót do starszego tagu po zmianie schematu nie jest bezpieczny. Zachowaj wykonaną przed chwilą kopię zapasową, dopóki nowa wersja nie będzie działać stabilnie przez dobę.
Gotify oraz Apprise
Gotify stanowi bardziej kompaktowe rozwiązanie: pojedynczy plik binarny z interfejsem WWW oraz aplikacją na system Android. Brak obsługi wieloznaczników w tematach (topic wildcards) oraz oficjalnego klienta na system iOS sprawia, że narzędzie to najlepiej sprawdza się w prywatnych środowiskach, gdzie jedynym celem jest system Android. Apprise to biblioteka języka Python oraz narzędzie wiersza poleceń, a nie serwer. Umożliwia ono rozsyłanie pojedynczej wiadomości do ponad stu usług, w tym ntfy, co jest przydatne w skryptach wymagających powiadomienia kilku odbiorców jednocześnie. ntfy zapewnia serwer, API HTTP oraz aplikacje na obie platformy mobilne, dlatego jest to najczęstszy wybór w przypadku powiadomień wysyłanych z wynajmowanego serwera.
FAQ
Dlaczego publikowanie na moim serwerze ntfy zwraca błąd 403?
Przy ustawieniu auth-default-access: "deny-all" w pliku server.yml, anonimowe publikowanie jest odrzucane i jest to zamierzone zachowanie. Należy wysłać poświadczenia za pomocą -u user:pass lub -H "Authorization: Bearer tk_...". Jeśli wysyłasz już token, a nadal otrzymujesz 403, użytkownik powiązany z tym tokenem nie posiada pasującego wpisu ACL dla danego tematu. Uruchom ntfy access, aby wyświetlić pełną listę. Pamiętaj, że uprawnienie write nie zezwala na subskrypcję, więc konto, które poprawnie publikuje wiadomości, nadal będzie otrzymywać odmowę przy próbie odczytu tego samego tematu.
Czy powiadomienia działają na iPhone z własnym serwerem ntfy?
Działają, za pośrednictwem przekaźnika, którego nie da się uniknąć. Apple wybudza aplikacje wyłącznie przez APNs (Apple push notification service), a wysyłać do niego może tylko wydawca aplikacji, dlatego ntfy przesyła poll_request zawierający identyfikator wiadomości do ntfy.sh, który przekazuje go dalej na urządzenie. Ustaw upstream-base-url: "https://ntfy.sh" w server.yml i zrestartuj kontener. Treść wiadomości jest nadal pobierana z Twojego serwera. Bez tego ustawienia powiadomienia na iOS są opóźnione lub w ogóle się nie pojawiają.
Dlaczego alert ntfy z mojego zadania cron nie dotarł?
Uruchom najpierw samą linię curl, aby sprawdzić, czy token i temat są poprawne. Jeśli działa ręcznie, a nie działa z cron, przyczyna leży przed wysłaniem alertu: cron uruchamia zadania w minimalnym środowisku z krótką zmienną PATH, więc skrypt wywołujący polecenie po samej nazwie może zakończyć działanie przed dotarciem do linii curl. Używaj ścieżek bezwzględnych, przekieruj wyjście zadania do pliku dziennika i odczytaj go po kolejnym uruchomieniu. Odpowiedź 429 zamiast dostarczenia oznacza, że działa limit szybkości (rate limit), a skrypt ponawia próby zbyt często.
Czy powinienem wystawiać ntfy na publiczny Internet?
Aplikacje mobilne muszą mieć dostęp do serwera z sieci komórkowych, więc publiczny punkt końcowy HTTPS z auth-default-access: "deny-all" oraz listami ACL dla poszczególnych tematów jest standardową konfiguracją. Jest to bezpieczne, o ile żaden temat nie jest dostępny do odczytu dla everyone. Instancja dostępna tylko przez VPN jest uzasadniona, gdy każdy subskrybent jest maszyną pod Twoją kontrolą. Jest to słabe rozwiązanie dla telefonów, ponieważ aplikacja otrzymuje powiadomienia tylko wtedy, gdy tunel jest aktywny, więc alerty są kolejkowane do momentu ponownego połączenia telefonu.