Crea tu propio servidor de notificaciones con Ntfy

Gestiona tus notificaciones con la privacidad que se merecen y desde cualquier dispositivo

Crea tu propio servidor de notificaciones con Ntfy

En varias entradas ya he comentado cómo recibir notificaciones en nuestra cuenta de Telegram, y si bien es un servicio que he estado usando de manera intensiva, siempre he tenido el miedo a qué pasaría si el día de mañana Telegram decide cerrar su servicio, o simplemente se cae y no puedo enterarme de lo que mis aplicaciones tratan de informarme... O simplemente, quiero manejar cierta información sensible que no me apetece que pase por las manos de Telegram o Google o Apple (para las notificaciones que recibo en el móvil).

Llevaba un tiempo detrás de esta idea y no voy a negar que, en varias ocasiones, he coqueteado con Gotify sin que, en ninguna de ellas, me convenciera demasiado. No me malinterpretéis, es un servicio de notificaciones genial y que funciona de manera muy fluida. Pero tiene ciertas características (aplicaciones por usuario, lecturas protegidas pero no escrituras en los tópicos, etc) que no me terminaban de convencer. Además, está bien para recibir notificaciones en el móvil, pero... se me antoja inútil sólo para eso.

Tras investigar durante un tiempo, dejar el tema, volver a retormarlo, volverlo a dejar... He terminado dando con Ntfy.sh (pronunciado notify según su creador). Se trata de un servicio de publicación-suscripción muy parecido a Gotify o, casi mejor, a MQTT (salvo que funciona sobre REST). Nos permite publicar y suscribirnos a tópicos sin necesidad de crearlos antes, además de soportar autenticación y roles (de manera básica por ahora). Y por si esto no fuera poco, tiene clientes para suscribirse y publicar desde prácticamente cualquier plataforma, lo que nos permite ampliar mucho su utilidad (podemos crear un script que se suscriba a un tópico y actúe en función de los mensajes que reciba, automatizar procesos, etc), además de que, en el caso de Android, nos puede servir como servidor de notificaciones, pues implementa el estándar Unified Push.

Me enrollo como las persianas, y la mejor manera de ver sus ventajas es que os cuente cómo instalarlo y algunos ejemplos de uso que le estoy dando. Así pues... ¡al lío!

Instalación y configuración de Ntfy.sh

Si bien se puede instalar como un simple binario (que además nos sirve igual como servidor que como cliente), en mi máquina lo tengo corriendo con Docker, por pura simplicidad de mantenimiento. No descarto el uso del binario en, por ejemplo, el ordenador, con el fin de usarlo como cliente y recibir notificaciones nativas, en lugar de necesitar del navegador como hago actualmente.

Comencemos creando nuestro fichero docker-compose.yml con la siguiente información:

version: "2.1"

services:
  ntfy:
    image: binwiederhier/ntfy
    container_name: ntfy
    restart: always
    command:
      - serve
    environment:
      - TZ=Europe/Madrid
    user: 1000:1000
    volumes:
      - ./cache:/var/cache/ntfy
      - .conf:/etc/ntfy
    networks:
      - tunnel
      
networks:
  tunnel:
    external:
      name: cloudflare-net
Fichero docker-compose.yml para Ntfy

Como se puede ver, el docker-compose.yml es muy sencillo. Apenas habrá que modificar el user:group para que cuadre con el que tengamos en nuestra máquina (lo cual podéis hacer simplemente ejecutando el comando id en Linux) y conectarlo a la red que prefiramos. El servicio se levanta en el puerto 80, por lo que si queremos acceder directamente a él, deberemos exponerlo. En mi caso, últimamente estoy haciendo bastante uso de los túneles de Cloudflare, pues me facilitan mucho la vida para probar estos pequeños desarrollos y, en algunos casos, para exponer servicios al exterior sin mayor complejidad.

Una vez tenemos nuestro fichero preparado, deberemos crear 2 carpetas: cache y conf. La primera servirá para almacenar los datos temporales de Ntfy, como las imágenes o ficheros que enviemos por la notificación (sí, podremos hacer eso), durante el tiempo que definamos en su configuración.

Por otro lado, la carpeta conf contendrá las bases de datos SQLite con la información de los usuarios dados de alta, los roles y permisos, y la información de los mensajes en caché. Además, tendremos ahí un fichero YML con la configuración del servidor, el cual deberá llamarse server.yml y del cual nos podemos bajar una plantilla de este enlace. Es bastante descriptivo y está muy bien documentado, así que sólo mencionaré lo más básico y necesario para tener nuestro servicio disponible lo antes posible:

  • base-url: Deberemos especificar aquí la URL pública de nuestro servidor Ntfy. Como dice en la documentación, será necesaria para usarlo con dispositivos iOS, así como para los enlaces de descarga de ficheros, el envío de correos electrónicos para mostrar como notificación, como puerta de entrada para gestionar las notificaciones en la red de Matrix...
  • cache-file: Ruta hasta el fichero cache.db dentro de nuestro contenedor. Según hemos mapeado las carpetas, ésta puede ser /etc/ntfy/cache.db.
  • cache-duration: El tiempo durante el cual se mantendrán los mensajes y ficheros en caché. Por defecto será de 12 horas si no especificamos nada.
  • auth-file: Ruta hasta el fichero que contiene la información de usuarios y roles. Al igual que en el caso de la caché, podemos especificar /etc/ntfy/auth.db
  • auth-default-access: El acceso por defecto a los tópicos de nuestro servidor. Si vamos a usarlo para nuestro propio fin privado, lo suyo es que definamos aquí deny-all. Será la configuración por defecto para los tópicos que no requieran de usuario y contraseña. También podemos especificar, si así lo preferimos, read-write (es el valor por defecto si no ponemos nada), read-only o write-only.
  • behind-proxy: Si el servicio está detrás de un proxy, como es mi caso con Cloudflare o si lo tenemos tras un Traefik o un Nginx simplemente, deberemos especificarlo con un true.
  • attachment-cache-dir: Ruta hacia el directorio donde se almancenarán los ficheros en caché. En base a nuestro docker-compose.yml, ésta puede ser /var/cache/ntfy.
  • attachment-total-size-limit: Será el límite del tamaño de nuestra caché en disco. Si no vamos a hacer un uso intensivo del almacenamiento, podemos limitarla a 1G perfectamente (y evitamos que se nos llene el disco por una mala gestión o acciones malintencionadas).
  • attachment-file-size-limit: Aquí indicaremos el tamaño máximo por fichero en la caché. Un tamaño de 100M es más que suficiente (sobre todo si pensamos enviar logs o imágenes).
  • attachment-expiry-duration: El tiempo durante el cual los ficheros permanecerán en la caché. Por defecto es de 12 horas. Yo aquí suelo poner el mismo tiempo que en cache-duration, pues para mí, en cuanto se borre el mensaje ya no será necesario el fichero, pero depende ya de cada uno y el uso que le quiera dar (ya que podremos copiar la URL del fichero y que éste esté disponible incluso cuando la notificación haya expirado).
  • upstream-base-url: URL para hacer uso de los servicios de notificaciones de Google/Apple (es decir, Firebase o APNS-connected, respectivamente). Podemos definir aquí el servidor de https://ntfy.sh si vamos a usar este servicio.
  • global-topic-limit: Límite de tópicos a crear antes de que el servidor rechace aceptar nuevos. Si no vamos a utilizar muchos, un valor de 20 o 30 puede ser más que suficiente.
  • log-level: Nivel de log de la aplicación. WARN o ERROR estará bien cuando usemos el servicio de manera continua y no necesitemos monitorizar qué ocurre.

Una vez tenemos nuestro fichero docker-compose.yml y el server.yml correctamente configurados, podemos arrancar la aplicación. Un simple docker compose up -d y podremos acceder vía web a través de la URL que le hayamos asignado (por ejemplo: https://ntfy.example.org/).

Gestión de usuarios y permisos

Una vez tenemos nuestro servidor de notificaciones corriendo, es buena idea crear los usuarios que vayamos a necesitar. Para ello accedemos a la consola (estando con Docker podemos hacerlo con el comando docker exec -ti ntfy sh) y una vez dentro, el comando ntfy --help  será nuestro amigo.

Todos los comandos, seguidos de un --help nos darán toda la información que necesitamos para utilizarlos. Así, ntfy --help nos informará de qué podemos hacer con él, mientras que ntfy access --help nos mostrará la información relevante al comando de control de permisos.

Usuarios

Para listar los usuarios, bastará con ejecutar el comando ntfy user list. Si queremos dar de alta un nuevo usuario, con ntfy user add pepito nos pedirá la contraseña para él, y si queremos que sea administrador, basta con ntfy user add --role=admin pepito. Para borrarlo, ntfy user del pepito, cambiar su contraseña ntfy user change-pass y su rol ntfy user change-role user|admin en función del que le queramos dar.

Hay un usuario reservado, que es everyone o *, el cual hará referencia a los usuarios anónimos.

Los usuarios con rol de administrador pueden leer y escribir en todos los tópicos, no necesitaremos crear una configuración para ellos.

Permisos

Para gestionar los permisos, el comando ntfy access nos mostrará la configuración actual. Pongamos por ejemplo que queremos que nuestro usuario Pepito pueda publicar y suscribirse a home-info, home-alertas, home-debug... Y cualquier otro tópico que empiece por home-. Puesto que esos tópicos los usará él y sólo él, haremos uso de ntfy access pepito "home-*" rw.

Por otro lado, resulta que también le hemos dado permisos, sin querer claro está, al tópico cosas-importantes, y no queremos que se suscriba más. Con un simple ntfy access --reset pepito cosas-importantes lo sacaremos del tópico.

Si queremos que nuestro servidor de notificaciones se pueda usar por aplicaciones en Android que soporten Unified Push... Algo tan simple como ntfy access everyone "up*" rw o ntfy access * "up*" rw será más que suficiente.

Una vez hemos terminado, tanto el comando ntfy user list como el comando ntfy access (son sinónimos en realidad) nos mostrarán algo como lo siguiente:

user administrador (admin)
- read-write access to all topics (admin role)
user pepito (user)
- read-write access to topic home-*
user * (anonymous)
- read-write access to topic up*
- no access to any (other) topics (server config)
Salida del comando ntfy access o el comando ntfy user list

Como veis, es bastante sencillo de manejar.

Suscribirse a tópicos

Los tópicos en Ntfy no son más que rutas en la URL, algo tan sencillo como suscribirse al tópico alertas se hace con la URL https://ntfy.example.org/alertas.

Interfaz web

Ntfy nos proporciona una interfaz web responsiva (accesible desde la URL principal), desde la cual nos podremos suscribir a los tópicos que deseemos, además de poder usar otros servidores (como el propio de https://ntfy.sh).

Interfaz web de Ntfy.sh
Interfaz web de Ntfy.sh

Dispositivos móviles

En Android, es bastante similar. Nos podemos bajar la aplicación desde la Google Play Store o desde F-Droid y suscribirnos a lo que nos interese, además de permitirnos integrarnos con más aplicaciones (como Tasker).

CLI y API

El servicio nos ofrece 2 maneras de consumir mensajes orientados a la automatización y la ejecución de scripts. Éstos son mediante el uso de un binario ntfy-cli o a través de su API.

Publicar en tópicos

Para publicar mensajes, tenemos varias opciones: Podemos hacer uso del binario de ntfy-cli que nos proporciona su desarrollador, podemos hacerlo desde la interfaz web o, directamente, mediante el uso del comando curl. También tenemos disponibles librerías para Go, Java, Python...

Un ejemplo muy sencillo, sería el uso del comando curl para enviar una notificación de prueba:

curl \
  -H "Title: Notificación de prueba" \
  -H "Priority: default" \
  -H "Tags: smile" \
  -d "Éste es un mensaje de prueba" \
  ntfy.example.org/test
Ejemplo de notificación simple

El resultado de ese comando se verá así en la web:

Notificación recibida en la interfaz web
Notificación recibida en la interfaz web
Mensaje popup de la notificación
Mensaje popup de la notificación

Pero podemos ir más allá. Podemos agregar tags que pueden convertirse en emoji en el mensaje, agregar el usuario y la contraseña si es un tópico protegido, establecer la prioridad de la notificación (del 1 al 5, siendo de menos a más, o mediante palabras reservadas: min, low, default, high o max|urgent). También podemos agregar imágenes, enlaces, indicar acciones al pulsar sobre la notificación... ¡Incluso podemos postponer las notificaciones a una hora determinada o pasado X tiempo!

También podemos definir botones de acción (por ejemplo, si recibimos una notificación de que la temperatura en casa se ha disparado, permitir apagar la calefacción desde ahí mismo, llamando a un endpoint, por ejemplo).

Las posibilidades que ofrece son inmensas, y os invito a pasaros por la documentación, pues es tan extensa que en una entrada sería inviable comentar todas las opciones. Además está soportado cada vez por más servicios (el más relevante para mí, Uptime Kuma).

En mi caso, lo estoy orientando a centralizar todas las notificaciones que ahora mismo tengo repartidas por los bot de Telegram, Matrix... Y además usarlo como servidor de notificaciones para Matrix y la red de Mastodon (aún estoy probando esta parte).

¡Y esto es todo por el momento! Seguramente en un futuro le dé mucho más uso, sobre todo cuando me meta con las automatizaciones, y ya os iré contando. De momento... me mantiene informado de todo lo que ocurre en cada momento en mis servicios, que ya es decir.

Si te ha gustado la entrada, o te ha sido útil y quieres ayudarme a pagar los gastos que conlleva el servidor y mantener así el blog libre completamente de anuncios, puedes hacer una donación en Bitcoin en la siguiente dirección:

Donar