5.5 KiB
title | date | draft | description | featured | toc | comment | series | tags | ||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
Easy Push Notifications With ntfy.sh | 2023-09-11 | true | Deploying and configuring a self-hosted pub-sub notification handler, getting another server to send a notifcation when it boots, and integrating the notification handler in Home Assistant. | false | true | true | Projects |
|
Wouldn't it be great if there was a simple way to send a notification to your phone(s) with a simple curl
call? Then you could get notified when a script completes, or a server reboots, a user logs in to a system, or a sensor connected to Home Assistant changes state. How great would that be??
ntfy.sh (pronounced notify) provides just that. It's an open-source, easy-to-use, HTTP-based pub-sub notification service, and it can notify using either mobile apps for Android (Play or F-Droid) or iOS (App Store) or a web app. I thought it sounded pretty compelling - and then I notice that ntfy's docs made it sound really easy to self-host the server component.
So let's take it for a spin!
Installation
I'm going to use the Docker setup on an existing cloud server and use Caddy as a reverse proxy.1
So I'll start by creating a new directory at /opt/ntfy/
to hold the goods, and create a compose config.
/opt/ntfy/docker-compose.yml
:
version: "2.3"
services:
ntfy:
image: binwiederhier/ntfy
container_name: ntfy
command:
- serve
environment:
- TZ=UTC # optional: set desired timezone
volumes:
- ./cache/ntfy:/var/cache/ntfy
- ./etc/ntfy:/etc/ntfy
- ./lib/ntf:/var/lib/ntfy
ports:
- 8080:80
healthcheck: # optional: remember to adapt the host:port to your environment
test: ["CMD-SHELL", "wget -q --tries=1 http://localhost:8080/v1/health -O - | grep -Eo '\"healthy\"\\s*:\\s*true' || exit 1"]
interval: 60s
timeout: 10s
retries: 3
start_period: 40s
restart: unless-stopped
This config will create/mount folders in the working directory to store the ntfy cache and config. It also maps localhost:8080
to port 80
on the container, and enables a simple healthcheck against the ntfy health API endpoint. This will ensure that the service stays healthy.
I can go ahead and bring it up:
sudo docker-compose up -d
I'll also want to add the following to my Caddy config:
/etc/caddy/Caddyfile
:
ntfy.example.com, http://ntfy.example.com {
reverse_proxy localhost:8080
@httpget {
protocol http
method GET
path_regexp ^/([-_a-z0-9]{0,64}$|docs/|static/)
}
redir @httpget https://{host}{uri}
}
And I'll restart Caddy to apply the config:
sudo systemctl restart caddy
Configuration
/opt/ntfy/etc/ntfy/server.yml
:
auth-file: "/var/lib/ntfy/user.db"
auth-default-access: "deny-all"
base-url: "https://ntfy.example.com"
sudo docker-compose down && sudo docker-compose up -d
sudo docker exec -it ntfy /bin/sh
ntfy user add --role=admin admin_user
ntfy user add writer
ntfy token add writer
ntfy access writer ping write-only
Usage
curl \
-u "writer:$password" \
-d "My first notification" \
https://ntfy.example.com/ping
curl \
-H "Authorization: Bearer $token" \
-H "Title: Here's a Message Title" \
-d "This is the message body" \
https://ntfy.example.com/ping
Notify on boot
/usr/local/bin/ntfy_push.sh
:
#!/usr/bin/env bash
curl \
-H "Authorization: Bearer $TOKEN" \
-H "Title: $1" \
-d "$2" \
https://ntfy.example.com/ping
/usr/local/bin/ntfy_boot_complete.sh
:
#!/usr/bin/env bash
TITLE="$(hostname -s)"
MESSAGE="System boot complete"
/usr/local/bin/ntfy_push.sh "$TITLE" "$MESSAGE"
/etc/systemd/system/ntfy_boot_complete.service
:
[Unit]
After=network.target
[Service]
ExecStart=/usr/local/bin/ntfy_boot_complete.sh
[Install]
WantedBy=default.target
sudo systemctl daemon-reload
sudo systemctl enable --now ntfy_boot_complete.service
Home Assistant
configuration.yaml
:
notify:
- name: ntfy
platform: rest
method: POST_JSON
authentication: basic
username: writer
password: $PASSWORD
data:
topic: ping
title_param_name: title
message_param_name: message
resource: https://ntfy.example.com
automations.yaml
:
- alias: Water Leak Detection
description: ''
trigger:
- platform: state
entity_id:
- binary_sensor.water_6
- binary_sensor.water_3
- binary_sensor.water_5
from: 'off'
to: 'on'
condition: []
action:
- service: notify.ntfy
data:
title: Leak detected!
message: '{{ trigger.to_state.attributes.friendly_name }} detected.'
-
I'm a big fan of Caddy. It may not be quite as capable/flexible as
nginx
but I love how simple it makes most configurations. Using Caddy in this will will not only enable HTTPS for the new web service but will also automatically obtain/renew LetsEncrypt certs so that I don't even have to think about it. ↩︎