Det här sätter du upp
ntfy (uttalas "notify") gör om en vanlig HTTP-förfrågan till en notis i mobilen eller webbläsaren. Ett skript, ett backupjobb eller ett övervakningsverktyg skickar en rad text till ett ämne, till exempel alerts-server, och varje enhet som prenumererar på ämnet visar den inom en sekund. Allt som kan köra curl eller anropa en webhook kan använda den.
ntfy utvecklas av Philipp C. Heckel som ett projekt med öppen källkod på GitHub, med dubbel licens: Apache License 2.0 och GNU General Public License 2.0 (GPLv2). Den publika servern ntfy.sh, som apparna använder om du inte lägger till en egen, drivs av ntfy LLC, vars villkor lyder under lagen i Connecticut i USA. Din egen server kontaktar ingen extern tjänst som standard: en avlyssning av dess trafik vid start och vid publicering visade inga anslutningar alls. Det enda undantaget, som du själv kan slå på, gäller iPhone och beskrivs i steg 11.
Här körs den från sin officiella avbild, i Podman under en egen användare som heter ntfy, bakom Caddy från Podman-guiden. Ingen kan läsa eller skicka något utan konto: ett konto publicerar, ett annat läser.
Varje steg nedan har körts på en nyinstallerad Melonslab VC-P Alloy (2 vCPU, 8 GB) med Debian 13:
- ntfy 2.28.0 svarade via Caddy med ett certifikat från Let's Encrypt. Utan konto fick publicering, läsning och en WebSocket-anslutning alla "403 forbidden" via Caddy.
- En token som bara får publicera kunde inte läsa, och kunde inte publicera till ämnen utanför sina egna. Kontot som läser kunde inte publicera.
- Meddelanden från
curl, från en SSH-inloggning, från en systemd-tjänst som fallerade, från Uptime Kuma 2 och från en Grafana-webhook kom fram på ämnet, i en webbläsare och i tre strömmande anslutningar som hölls öppna via Caddy i nästan två minuter. - ntfy såg varje besökares riktiga adress, över IPv4 och IPv6, och struntade i en förfalskad
X-Forwarded-For-header. Gränserna svarade "429" efter en skur av förfrågningar och efter upprepade felaktiga lösenord. - Användare, åtkomstregler och token återställdes från en säkerhetskopia till en tom volym, och allt startade igen av sig självt efter en omstart av servern, med sparade meddelanden kvar.
ntfy använde omkring 18 MB minne.
Innan du börjar
Du behöver:
- en server uppsatt som i Podman-guiden, med Caddy igång;
- en A-post och en AAAA-post för
ntfy.example.comsom pekar på din server; - för larmen från SSH och systemd,
curlpå servern:apt install -y curl.
Exemplen använder ntfy.example.com för ntfy och 203.0.113.10 för din server. Byt ut dem genomgående.
1. Skapa användaren
Som root:
useradd -m -s /bin/bash ntfy
loginctl enable-linger ntfy
machinectl shell ntfy@
Allt fram till steg 4 körs som ntfy.
2. Skriv konfigurationen
mkdir -p ~/ntfy ~/.config/containers/systemd
Skapa ~/ntfy/server.yml:
base-url: "https://ntfy.example.com"
listen-http: ":80"
# Caddy passes on each visitor's address in X-Forwarded-For.
behind-proxy: true
# Keep messages for a day, on disk, so that phones that were offline get them later.
cache-file: "/var/lib/ntfy/cache.db"
cache-duration: "24h"
# Nobody gets in without an account, and accounts only reach the topics you allow.
auth-file: "/var/lib/ntfy/user.db"
auth-default-access: "deny-all"
enable-login: true
Vad raderna gör:
behind-proxy: truefår ntfy att ta besökarens adress från headernX-Forwarded-For, som Caddy sätter. Utan den verkar varje förfrågan komma från serverns egen IPv4-adress,203.0.113.10, eftersom pasta skickar vidare Caddys anslutningar så, och då delar alla besökare på samma gränser. Caddy ersätter enX-Forwarded-Forsom en besökare skickar, så headern går inte att förfalska utifrån.cache-fileochcache-durationsparar varje meddelande i 24 timmar, i en liten SQLite-fil. En mobil som var offline hämtar det den missade när den ansluter igen. Utancache-fileligger cachen bara i minnet och försvinner när ntfy startas om. Medcache-duration: "0"sparas ingenting alls.auth-default-access: "deny-all"stänger varje ämne för alla utan konto. ntfy harread-writesom standard, vilket låter vem som helst på internet läsa och skriva i alla ämnen på din server.enable-login: truelåter dig logga in i webbappen, i steg 10.
Bilagor är avstängda: ntfy tar bara emot filer när attachment-cache-dir är satt, och svarar annars "attachments not allowed". Samma sak gäller ett meddelande över 4 KB, som ntfy annars skulle göra om till en bilaga. Larm är korta, så det finns inget att lagra.
3. Definiera containern
Skapa ~/.config/containers/systemd/ntfy.container:
[Unit]
Description=ntfy
[Container]
ContainerName=ntfy
Image=docker.io/binwiederhier/ntfy:v2
Exec=serve
Volume=%h/ntfy/server.yml:/etc/ntfy/server.yml:ro
Volume=ntfy-data:/var/lib/ntfy
# Only Caddy, on this server, can reach ntfy: the port is not open to the internet.
PublishPort=127.0.0.1:8107:80
AutoUpdate=registry
[Service]
Restart=always
[Install]
WantedBy=default.target
Taggen v2 följer varje 2.x-version. Starta den:
systemctl --user daemon-reload
systemctl --user start ntfy
systemctl --user enable --now podman-auto-update.timer
curl -s http://127.0.0.1:8107/v1/health
Det sista kommandot skriver ut {"healthy":true}.
4. Sätt Caddy framför
Gå tillbaka till root med exit, byt till machinectl shell caddy@ och lägg till det här blocket sist i ~/Caddyfile:
ntfy.example.com {
reverse_proxy 127.0.0.1:8107
}
Starta om Caddy med systemctl --user restart caddy.
Mobiler och webbläsare håller en anslutning öppen mot ntfy och väntar på meddelanden, över en WebSocket eller en lång HTTP-ström. Caddy skickar vidare båda utan extra inställningar, och ntfy skickar en keepalive var 45:e sekund så att inget på vägen stänger en anslutning som är tyst.
5. Skapa konton och åtkomstregler
Exemplen använder två konton:
alertspublicerar. Skript, Uptime Kuma och Grafana använder dess token, aldrig ett lösenord.annaläser. Det använder du i webbläsaren och i mobilen.
Båda får ämnena som börjar med alerts-, som alerts-server och alerts-kuma, och inget annat. Som root byter du till machinectl shell ntfy@ och skapar dem. Varje konto frågar efter ett lösenord två gånger:
podman exec -it ntfy ntfy user add anna
podman exec -it ntfy ntfy user add alerts
podman exec ntfy ntfy access anna 'alerts-*' read-only
podman exec ntfy ntfy access alerts 'alerts-*' write-only
podman exec ntfy ntfy token add --label=scripts alerts
Det sista kommandot skriver ut token, som börjar med tk_. Spara den: det är den dina skript skickar. Kontrollera reglerna:
podman exec ntfy ntfy access
user alerts (role: user, tier: none)
- write-only access to topic alerts-*
user anna (role: user, tier: none)
- read-only access to topic alerts-*
user * (role: anonymous, tier: none)
- no topic-specific permissions
- no access to any (other) topics (server config)
En token kan göra allt dess konto kan, så en token för alerts kan bara publicera till alerts-*. Om en läcker tar du bort den med podman exec ntfy ntfy token remove alerts tk_... och skapar en ny. ntfy token list visar varje token med adressen den senast användes från.
6. Skicka en notis
Från valfri dator, med din token i stället för tk_...:
curl -H "Authorization: Bearer tk_..." \
-H "Title: Disk almost full" \
-H "Priority: high" \
-H "Tags: warning" \
-d "/var is at 91%" \
https://ntfy.example.com/alerts-server
ntfy svarar med meddelandet som JSON. Headrarna är valfria:
Priorityär en avmin,low,default,highochurgent, eller 1 till 5. Mobilen kan spela olika ljud för varje nivå, och webbappen kan dölja de låga.Tagstar en kommaseparerad lista. Taggar som är namn på emoji, somwarning,keyellerrotating_light, visas som emojin framför rubriken.
Utan token vägrar ntfy, både för publicering och läsning:
curl -d "hello" https://ntfy.example.com/alerts-server
{"code":40301,"http":403,"error":"forbidden","link":"https://ntfy.sh/docs/publish/#authentication"}
7. Få ett larm när någon loggar in över SSH
Token ligger i en enda fil som bara root kan läsa. Som root, med din token:
install -m 600 /dev/null /etc/ntfy.env
cat > /etc/ntfy.env <<'EOF'
NTFY_URL=https://ntfy.example.com/alerts-server
NTFY_TOKEN=tk_...
EOF
Skapa /usr/local/sbin/ntfy-ssh-login:
#!/bin/sh
# Called by PAM for every SSH session; only act when one opens.
[ "$PAM_TYPE" = "open_session" ] || exit 0
. /etc/ntfy.env
curl -s -m 5 -o /dev/null \
-H "Authorization: Bearer $NTFY_TOKEN" \
-H "Title: SSH login on $(hostname)" \
-H "Tags: key" \
-d "$PAM_USER logged in from $PAM_RHOST" \
"$NTFY_URL" &
exit 0
Gör det körbart och låt SSH köra det vid varje inloggning:
chmod 755 /usr/local/sbin/ntfy-ssh-login
echo "session optional pam_exec.so /usr/local/sbin/ntfy-ssh-login" >> /etc/pam.d/sshd
Logga in från en annan terminal. Ett meddelande som root logged in from 198.51.100.7 kommer fram. Tack vare optional och & väntar en inloggning aldrig på ntfy och misslyckas inte på grund av den: med ntfy stoppad tog vår testinloggning ändå 1,3 sekunder.
8. Få ett larm när en tjänst fallerar
systemd kan starta en annan enhet när en fallerar, med OnFailure=. Skapa /usr/local/sbin/ntfy-unit-failed, som skickar den fallerade enhetens status och dess sista loggrader:
#!/bin/sh
. /etc/ntfy.env
systemctl status --no-pager --lines=5 "$1" | curl -s -m 10 -o /dev/null \
-H "Authorization: Bearer $NTFY_TOKEN" \
-H "Title: $1 failed on $(hostname)" \
-H "Priority: high" \
-H "Tags: rotating_light" \
--data-binary @- \
"$NTFY_URL"
Och en mallenhet som kör det, /etc/systemd/system/ntfy-failed@.service:
[Unit]
Description=Send an ntfy alert that %i failed
[Service]
Type=oneshot
ExecStart=/usr/local/sbin/ntfy-unit-failed %i
chmod 755 /usr/local/sbin/ntfy-unit-failed
systemctl daemon-reload
Lägg sedan till en rad i avsnittet [Unit] för varje tjänst du vill få larm om. För en backuptjänst öppnar systemctl edit backup.service en tilläggsfil. Lägg till:
[Unit]
OnFailure=ntfy-failed@%n.service
%n är tjänstens fullständiga namn. För att prova finns här en tjänst som alltid fallerar, /etc/systemd/system/demo-backup.service:
[Unit]
Description=Demo backup that fails
OnFailure=ntfy-failed@%n.service
[Service]
Type=oneshot
ExecStart=/bin/sh -c "echo backup target unreachable; exit 1"
systemctl daemon-reload
systemctl start demo-backup.service
Starten misslyckas, och ett meddelande med hög prioritet och rubriken demo-backup.service failed on ... kommer fram, med backup target unreachable bland loggraderna. Ta bort testtjänsten efteråt med rm /etc/systemd/system/demo-backup.service och systemctl daemon-reload.
9. Larm från Uptime Kuma och Grafana
Uptime Kuma, från Uptime Kuma-guiden, har stöd för ntfy inbyggt. Öppna Settings, Notifications, välj Set Up Notification och:
- sätt Notification Type till Ntfy;
- ange
alerts-kumasom ntfy Topic ochhttps://ntfy.example.comsom Server URL; - sätt Authentication Method till Access Token och klistra in token;
- slå på Default enabled och Apply on all existing monitors, välj Test och sedan Save.
Testmeddelandet ntfy Testing kommer fram på alerts-kuma. När en bevakning gick ner i vårt test skickade Kuma Closed port test Down [Uptime-Kuma] med texten Connection failed. Om Kuma körs på samma server behöver den containers.conf från steg 2 i sin guide för att nå ntfy.example.com.
Grafana och andra verktyg som skickar webhooks postar JSON. ntfy har en inbyggd mall som gör om Grafanas larm till en läsbar rubrik och text: lägg till ?template=grafana i ämnets adress. Vi testade det med Grafana-exemplet från ntfys dokumentation, inte från en Grafana som kördes:
curl -H "Authorization: Bearer tk_..." \
--data-binary @grafana-alert.json \
"https://ntfy.example.com/alerts-grafana?template=grafana"
Meddelandet kom fram med rubriken 🚨 [FIRING:1] Load avg 15m too high, och ✅ [RESOLVED] ... när larmet hade upphört. ntfy har också mallar för github och alertmanager.
Ett verktyg som bara tar en adress, och inte kan skicka en Authorization-header, kan lägga token i adressen i stället. Koda headerns värde:
printf 'Bearer tk_...' | base64 -w0 | tr -d '='
och lägg till resultatet som auth=, som i https://ntfy.example.com/alerts-grafana?template=grafana&auth=QmVhcmVy.... Adressen är då lika hemlig som token.
10. Läs dem i webbläsaren
Öppna https://ntfy.example.com/login och logga in som anna med Sign in. Välj Subscribe to topic, ange alerts-server och välj Subscribe. Tidigare meddelanden från cachen visas direkt, och nya inom en sekund, så länge fliken är öppen. Webbappen sparar dina prenumerationer i ditt konto, så de dyker upp i varje webbläsare där du loggar in. Välj Grant now högst upp för att låta webbläsaren visa skrivbordsnotiser.
Webbappen laddar ingenting från andra servrar. ntfy kan också leverera till webbläsaren när fliken är stängd, via webbläsartillverkarens pushtjänst (för Chrome, Googles). Det kräver extra nycklar i server.yml och är avstängt här.
11. Läs dem i mobilen
En mobil går inte att testa på en server, så det här steget följer ntfys dokumentation, och vi har inte provat det på en enhet.
Android. Installera ntfy från Google Play eller F-Droid. Lägg till din server och användaren anna i appen och prenumerera sedan på alerts-server. Googles Firebase är inte inblandat: versionen från Google Play använder det bara för ntfy.sh, aldrig för din egen server, och versionen från F-Droid innehåller det inte alls. Mot din server håller appen en egen anslutning öppen, vilket ntfy kallar instant delivery, och visar en permanent notis så länge den gör det.
iPhone. iOS låter inte appen hålla en egen anslutning öppen i bakgrunden, så notiser direkt kräver Apples pushtjänst, som ntfy når via ntfy.sh. Utan den kommer meddelandena enligt ntfys dokumentation fram till slut, men det kan ta timmar, och oftast högst 20 till 30 minuter när du använder telefonen. För att få dem direkt lägger du till den här raden i server.yml och startar om ntfy:
upstream-base-url: "https://ntfy.sh"
Med den skickar din server en pollförfrågan till ntfy.sh för varje meddelande: meddelandets ID och SHA-256-hashen av ämnets adress, från serverns adress, och aldrig själva meddelandet. Vi kontrollerade det på testservern: ntfy kontaktade ntfy.sh först när raden hade lagts till, och hashen i loggen var SHA-256 av https://ntfy.example.com/alerts-server. ntfy.sh skickar förfrågan vidare till Googles Firebase och Apples pushtjänst, som väcker appen med texten "New message". Appen hämtar sedan det riktiga meddelandet från din server, med ditt konto. Låt bli raden om ingen använder iPhone.
12. Gränser
ntfy begränsar varje besökare per adress, utöver åtkomstreglerna. Standardvärdena passar en privat server:
- 60 förfrågningar i en skur, sedan en till var 5:e sekund. Vår skur fick "429 too many requests" när de var förbrukade.
- Omkring 30 felaktiga lösenord eller token, sedan "429 too many auth failures" en stund.
Varje IPv6-nät av storleken /64 räknas som en besökare. Gränserna är inställningar i server.yml, som visitor-request-limit-burst, och ntfys dokumentation om konfigurationen listar alla.
13. Säkerhetskopiera
ntfy sparar konton, åtkomstregler och token i user.db, och de sparade meddelandena i cache.db, båda i sin volym. Som ntfy:
mkdir -p ~/backup
systemctl --user stop ntfy
podman volume export ntfy-data --output ~/backup/ntfy-data.tar
cp ~/ntfy/server.yml ~/backup/
systemctl --user start ntfy
För att återställa, som ntfy:
systemctl --user stop ntfy
podman rm -f ntfy
podman volume rm ntfy-data
podman volume create ntfy-data
podman volume import ntfy-data ~/backup/ntfy-data.tar
systemctl --user start ntfy
podman exec ntfy ntfy access visar då samma användare och regler, och de gamla token fungerar fortfarande. Säkerhetskopian innehåller token och lösenordshashar: kopiera ~/backup till en annan maskin och förvara den säkert.
14. Uppdatera
podman-auto-update.timer från steg 3 letar efter en ny v2-avbild en gång om dagen och startar om ntfy på den. För att se vad den skulle göra just nu:
podman auto-update --dry-run
För att uppdatera direkt kör du podman auto-update. ntfy har sina data i volymen, så en uppdatering tappar ingenting.
Felsökning
Allt får "403 forbidden", även med token. Ämnet ligger utanför vad kontot får använda: token för alerts kan bara publicera till ämnen som börjar med alerts- och kan inte läsa dem. podman exec ntfy ntfy access visar reglerna.
Alla besökare har serverns egen adress i ntfys logg, och ett flitigt skript stoppar de andra med "429". behind-proxy: true saknas i server.yml, eller så startades ntfy inte om efter att raden lades till.
En fil eller ett långt meddelande får "attachments not allowed". Bilagor är avstängda, som i steg 2, och en text över 4 KB räknas som en bilaga. Skicka mindre text, till exempel de sista raderna i en logg i stället för hela loggen.
Meddelandena är borta efter en omstart. cache-file saknas, så ntfy höll dem bara i minnet.