Skip to main content

The Versatile ntfy

ntfy Logo ( ntfy repo )

Ever executed a long terminal command *multi-stage docker build* and forgot about it? ever wished you could easily get notified after a job or task fails *pg_dumpall*?

Introduction

Notification systems can be rather tedious to set up, annoying to have on different platforms or simply lack flexibility.

PostgreSQL backup

Let’s take an example, we have a script that looks like this:

# /usr/local/sbin/pg_backup.sh

PG_HOST=localhost
PG_USER=postgres
PG_PORT=5432
PG_DB=postgres

BACKUP_DIR="/tmp/postgres/backup"

mkdir -p "${BACKUP_DIR}"
pg_dump -U "${PG_USER}" -d "${PG_DB}" -p "${PG_PORT}" -h "${PG_HOST}" > "${BACKUP_DIR}"/backup_"${PG_DB}".dump

Create a folder for logs and give postgres the correct permissions

sudo mkdir /var/log/postgres/ -p
sudo chown postgres:postgres /var/log/postgres/
This is only for demonstration, do NOT use this for actual PostgreSQL backups!

Now let’s create a simple cronjob to schedule this script

sudo -u postgres crontab -e
I personally prefer tying such jobs to their actual users, hence why I did the sudo -u postgres

Put this into it:

0 0 * * *  /usr/local/sbin/pg_backup.sh > /var/log/postgres/pg_backup.log 2>&1
It’s a good idea to timestamp your logs in the filename
For a critical task like a database backup, I prefer using a systemd timer

I have a whole article on that here

Now, how would we get notified if this particular job failed? And preferably attach some logs when it does.

Answer: you guessed it, ntfy

ntfy is not the only tool, but it’s one that I think deserves more attention for how simple it is

ntfy

What is ntfy

From the official website :

ntfy (pronounced notify) is a simple HTTP-based pub-sub notification service. It allows you to send notifications to your phone or desktop via scripts from any computer, and/or using a REST API. It’s infinitely flexible, and 100% free software.

This sounds rather confusing and doesn’t convey, in my opinion, just how much this little guy is hiding.

At its core ntfy is a pub-sub service; You have queues or topics or whatever you want to call them, you push notifications into them and whoever is subscribed can get notified when something happens.

However it is ABLE to connect to a topic via a websocket for realtime notifications, customize the authentication and restrict access to topics following a pattern (for example: topic names starting with private_ can only be seen by the admin user), schedule messages into the future and more.

And my favourite part is that the publishers don’t even need to install anything, you can simply post notifications using curl or wget or a python script and it would still work. Although you can use the official ntfy binary which is very pleasant.

How to deploy it

I will assume you have docker installed and configured, because we will be deploying it within docker for simplicity
  • Create an ntfy directory / folder in your working environment, I’ll put mine into ~/Projects/blog/ntfy/
  • Create a compose.yml file
  • Put this into it (which I got from the official website ):
services:
  ntfy:
    image: binwiederhier/ntfy
    container_name: ntfy
    command:
      - serve
    environment:
      - TZ=YOUR_TIMEZONE      # optional: set desired timezone
    volumes: # I modified these to suit my needs, but feel free to use whatever you like
      - ./cache:/var/cache/ntfy
      - ./ntfy:/var/lib/ntfy
      - ./server.yml:/etc/ntfy/server.yml
    ports:
      - 8080:80
    healthcheck: # optional: remember to adapt the host:port to your environment
        test: ["CMD-SHELL", "wget -q --tries=1 http://localhost:80/v1/health -O - | grep -Eo '\"healthy\"\\s*:\\s*true' || exit 1"]
        interval: 60s
        timeout: 10s
        retries: 3
        start_period: 40s
    restart: unless-stopped
    init: true # needed, if healthcheck is used. Prevents zombie processes
  • Create two directories, ntfy and cache into the same folder
  • Next, create a server.yml file and put this into it:

You can use the config generator here for this

# Server
base-url: "http://localhost:8080"
# Change this to true if you want a reverse proxy, you will have to configure it
behind-proxy: false

# Access control
auth-file: "/var/lib/ntfy/auth.db"
auth-default-access: "deny-all"
# This creates an admin user with `admin` password 
auth-users:
  - "admin:$2b$10$1AnX6EAXnojzz6KFJjsjaejk4/o4.SPY0o0G6aVXQDb3ZSY6FmJoW:admin"
enable-login: true
require-login: true
# We don't want new users creating accounts on our private server
enable-signup: false

# Attachments
attachment-cache-dir: "/var/cache/ntfy/attachments"

# Message cache
cache-file: "/var/cache/ntfy/cache.db"

# Email notifications (outgoing)
smtp-sender-addr: "smtp_domain_if_you_want_to_have_emails:587"
# This part can be something different if your mail server supports it
smtp-sender-from: "smtp_user_from"
smtp-sender-user: "smtp_user_from"
smtp-sender-pass: "token_or_password"

# Require verified recipient addresses for email notifications
smtp-sender-verify: true

# Message Delay
## the limit to how long you can 'schedule' your notifications into the future
message-delay-limit: 3d
  • Then docker compose up -d and you’re good!

How to use it

  • Open your web browser and navigate to http://localhost:8080/
  • Login with admin as username and password
  • Click on Subscribe to topic button on the left panel
  • Input reminders into the field and hit Subscribe
  • Send a curl command like this from your terminal:
curl -H "Title: Notification title" -u admin:admin http://localhost:8080/reminders -d "Notification Body here" 
  • You should now see a notification in your browser

Back to our PostgreSQL script

We could create a topic named backups or logs or events or whatever you like, I’ll just reuse our existing reminders topic.

Let’s change it with this

I have intentionally changed the PG_DB variable to non_existent so the backup fails.
# /usr/local/sbin/pg_backup.sh

PG_HOST=localhost
PG_USER=postgres
PG_PORT=5432
PG_DB=non_existent

BACKUP_DIR="/tmp/postgres/backup"

mkdir -p "${BACKUP_DIR}"
if ! pg_dump \
    -U "${PG_USER}" \
    -d "${PG_DB}" \
    -p "${PG_PORT}" \
    -h "${PG_HOST}" > "${BACKUP_DIR}"/backup_"${PG_DB}".dump 2>"${BACKUP_DIR}"/"${PG_DB}".log; then
        curl -X PUT \
             -u admin:admin \
             -H "Title: PostgreSQL Backup Failed" \
             -H "Message: Backup for ${PG_DB} failed. See log file for more info" \
             -H "Filename: ${PG_DB}.log" \
             -T "${BACKUP_DIR}/${PG_DB}.log" localhost:8080/reminders
fi
Here I intentionally used a different way of publishing a notification so you can see that you do have flexibility in doing things
Having your credentials exposed like this can be dangerous, you can read in the

official documentation about how to use tokens and configure your client.

This will try to backup a non existant database non_existent which will result in a failure and a notification. Check your browser and you’ll see the log file attached and you can simply download it and view it.

Usages

As I kept saying, ntfy is very flexible and very permissive in the way you use it.

Here I will lay down some personal ways where I use ntfy, this is simply to give ideas and is not meant as a guide

  • Sharing files between my laptop and phone quickly, the phone app is pretty good.
  • I used it for a personal multiplayer game where I create a topic when a lobby is created, you share topic name with your friends and the info is there to join, not efficient or safe but fun.
  • Receiving alerts like I just showed you when something in my VPS goes down.
  • Reminding myself to do tasks in the future
  • Creating reminders from my personal caldav calendar using a custom python package called caldav2ntfy .

Conclusion

ntfy is basically very versatile and that’s what I love about it, it doesn’t restrict you, it gives you topics and an ACL and you just do whatever you like with it.

I highly recommend you check out the official documentation which explains more about the features and how to configure ACLs which I did not get into in this post.