El problema: demasiadas herramientas de monitoreo, demasiados canales de notificación
Uptime Kuma vigila sus servicios, Beszel vigila las métricas de su VPS, Backrest gestiona sus copias de seguridad, y sus cron jobs callan por la noche — salvo cuando fallan. Cada uno tiene su canal de notificación: Slack, correo, webhook, Telegram. Resultado: cinco integraciones distintas que mantener, cinco tipos de alerta que formatear y ninguna vista unificada cuando algo falla a las 03:00. ntfy resuelve el problema al revés: es usted quien aloja el hub de notificaciones, y cada herramienta o script le envía sus alertas por HTTP puro.
Lo que ntfy aporta de entrada
- Envío de una notificación con un simple curl —
curl -d "Build fallido" https://ntfy.ejemplo.com/alertas-ci— cero SDK, cero bibliotecas. - Recepción en Android (F-Droid / Play Store), iOS (App Store), navegador de escritorio e interfaz web de ntfy — todos se suscriben al mismo topic vía WebSocket o SSE.
- Acciones dentro de la notificación — botones que disparan callbacks HTTP, abren URL o ejecutan comandos en el dispositivo.
- Notificaciones programadas — entrega diferida (
X-Delay: 30min) para programar recordatorios desde sus scripts. - Niveles de prioridad (mín. / baja / predeterminada / alta / urgente) con anulación del modo No molestar para las alertas críticas.
- Archivos adjuntos — imágenes o archivos de hasta 15 MB mediante la cabecera
X-Attacho una subida multipart. - Notificaciones SMTP — ntfy puede enviar un correo de respaldo si su móvil está desconectado o la app no está instalada.
- Control de acceso granular — usuarios, contraseñas cifradas con bcrypt, tokens de acceso y ACL por topic.
- < 30 MB de RAM, SQLite, un solo contenedor — el servidor de notificaciones push más ligero de su categoría.
Arquitectura: un pub/sub HTTP en un solo contenedor
ntfy es un binario Go que cabe en un único contenedor Docker. Expone una interfaz HTTP sencilla: un PUT o un POST sobre /su-topic publica un mensaje; cualquier cliente que escuche ese topic lo recibe al instante. Los mensajes se guardan en caché en SQLite con una retención configurable (12 horas por defecto). En producción, ntfy se coloca detrás de un proxy inverso nginx que gestiona el TLS — behind-proxy: true en server.yml indica a ntfy que lea la IP real desde X-Forwarded-For. Toda la configuración avanzada vive en un único archivo /etc/ntfy/server.yml.
Desplegar ntfy en producción: 8 pasos
Desplegar ntfy desde el marketplace ServOrbit
Abra su panel de ServOrbit → Marketplace → Monitoring → ntfy → Desplegar. Docker descarga
binwiederhier/ntfy:v2.30.0, arranca ntfy en el puerto 8080 y la interfaz web queda accesible en menos de 5 segundos con un certificado Let's Encrypt en su dominio.Enviar su primera notificación
Desde cualquier terminal con curl:
curl -d "Hola desde mi VPS" https://ntfy.ejemplo.com/mi-topic
Abra la interfaz web de ntfy, vaya ami-topicy su mensaje aparece en tiempo real.Crear el server.yml completo
Monte un archivo de configuración en el contenedor:
base-url: https://ntfy.ejemplo.comlisten-http: ":8080"behind-proxy: truecache-file: /var/lib/ntfy/cache.dbcache-duration: 12hauth-file: /var/lib/ntfy/user.dbauth-default-access: deny-allsmtp-sender-addr: smtp.ejemplo.com:587smtp-sender-user: [email protected]smtp-sender-pass: contraseñaSMTPsmtp-sender-from: [email protected]
Reinicie el contenedor tras cualquier cambio:docker restart ntfy.
Para recibir notificaciones también en iOS, añadaupstream-base-url: "https://ntfy.sh"a este archivo: la sección «iOS» más abajo explica por qué.Crear usuarios y tokens de acceso
# Crear un usuario admin interactivo
docker exec -it ntfy ntfy user add --role=admin admin
# Crear un usuario para scriptsdocker exec -it ntfy ntfy user add scripts
# Generar un token de accesodocker exec -it ntfy ntfy token add scripts
# Salida: tk_AbCdEf123456...
# Conceder lectura/escritura sobre un topicdocker exec -it ntfy ntfy access scripts server-alerts rwSuscribirse desde su teléfono con autenticación
Instale la app ntfy (Android: F-Droid o Play Store; iOS: App Store). Pulse + → introduzca la URL del servidor y el topic. Cuando ntfy solicite credenciales, introduzca el nombre de usuario y la contraseña. Recibirá push mediante UnifiedPush, FCM o APNs. En iOS, el push instantáneo solo llega si su servidor tiene
upstream-base-urldefinido (vea la sección dedicada más abajo).Alertar desde cron jobs usando un token
#!/bin/bash
NTFY_URL="https://ntfy.ejemplo.com"NTFY_TOKEN="tk_AbCdEf123456"/usr/local/bin/backup.sh; STATUS=$?if [ $STATUS -eq 0 ]; thencurl -s -H "Authorization: Bearer $NTFY_TOKEN" -H "Title: Backup nocturno" -H "Priority: default" -d "Backup completado a las $(date)" "$NTFY_URL/cron-jobs"elsecurl -s -H "Authorization: Bearer $NTFY_TOKEN" -H "Title: Backup FALLIDO" -H "Priority: urgent" -d "Backup fallido — código $STATUS" "$NTFY_URL/cron-jobs"fiConfigurar Backrest y Uptime Kuma
Backrest: UI → Planes → su plan → Hooks → Añadir Hook → tipo
Webhook (ntfy)→ URL del topic y token Bearer.Uptime Kuma: Ajustes → Notificaciones → Nuevo → tipo
ntfy→ URL del servidor → topicserver-alerts→ usuario/contraseña. Pruebe la notificación desde la UI.Grafana: Contact point → webhook → URL
https://ntfy.ejemplo.com/grafana→ cabecerasAuthorization: Bearer tk_...yContent-Type: text/plain.Activar notificaciones de correo de respaldo (SMTP)
Una vez configurado
smtp-sender-addrenserver.yml, envíe una notificación con la cabeceraX-Email:curl -H "Authorization: Bearer $NTFY_TOKEN" -H "Title: Alerta crítica" -H "X-Email: [email protected]" -d "Disco lleno al 95%" https://ntfy.ejemplo.com/server-alerts
ntfy envía el push Y el correo electrónico.
Autenticación y tokens de acceso: nunca exponga un topic público
Sin configuración, ntfy arranca sin control de acceso (la doc indica auth-default-access en read-write) — cualquiera que conozca la URL puede leer y escribir. En producción: auth-default-access: deny-all cierra el servidor; usuarios con contraseñas bcrypt en user.db; tokens de acceso vinculados a un usuario específico y revocables de forma independiente. Un token comprometido se revoca sin cambiar la contraseña del usuario.
Configuración multi-tenant: un servidor, varios equipos
Cree un usuario por servicio o equipo, concédale derechos solo sobre sus topics, y cada servicio creerá tener su propio hub. El rol admin ve todo; los usuarios normales están limitados a sus ACL. Para equipos con aislamiento total, despliegue dos contenedores ntfy en dos subdominios distintos.
Consejo: los nombres de topic como espacios de nombres y estrategia de silencio nocturno
ci-builds para GitHub Actions/GitLab CI, server-alerts para Uptime Kuma/Beszel, cron-jobs para scripts por lotes, backups para Backrest. La app ntfy le permite silenciar ciertos topics por la noche mientras mantiene server-alerts en alerta máxima con anulación de No molestar.
Resolución de problemas: 5 errores reales en producción
1. HTTP 403 Forbidden al publicar o suscribirse
Mensaje: {"code":40301,"http":403,"error":"Unauthorized"}
Causa: auth-default-access: deny-all activo y el token falta o ha caducado. Regenere el token con ntfy token add <usuario> y actualice sus scripts.
2. Notificación enviada pero nunca recibida en el móvil
En Android, compruebe primero que la app está suscrita a su instancia y no al servidor público ntfy.sh (URL en los ajustes de la app). En iOS, la causa más probable es la ausencia de upstream-base-url: "https://ntfy.sh" en server.yml: sin ese relé, la doc advierte de que la entrega puede tardar horas. Configúrelo y repase la checklist de la sección iOS más abajo. base-url también debe estar definido, pero la doc lo asocia a las URL de adjuntos y al pie de los correos, no a la llegada del push.
3. Enlaces incorrectos en los correos salientes
Sin base-url, el pie del correo (enlace a la notificación) se construye sin la URL pública y no sirve desde el exterior. Solución: establecer base-url: https://ntfy.ejemplo.com y reiniciar el contenedor.
4. Caídas repetidas de WebSocket en producción (reconexiones cada 30 s)
Causa: el timeout keepalive de nginx es inferior al intervalo de heartbeat de ntfy (keepalive-interval: "45s" en la configuración de ejemplo de la doc). Añada proxy_read_timeout 60; y proxy_send_timeout 60; al bloque location. Verifique también proxy_http_version 1.1; y proxy_set_header Upgrade $http_upgrade;.
5. OOM (Out of Memory) en un topic de alto volumen
Reduzca cache-duration a 1h o 30m. Filtre en el script (| grep -E 'ERROR|WARN') para enviar solo eventos relevantes. ntfy puede usar PostgreSQL en lugar de SQLite para sus almacenes (vea la doc de configuración).
iOS: por qué un ntfy autoalojado pasa por ntfy.sh para el push
En iOS una app no puede mantener una conexión abierta en segundo plano: la documentación de ntfy explica que esta restricción hace imposible un push instantáneo sin un servidor central. Su servidor autoalojado reenvía por tanto, por cada mensaje, una petición de «poll» a un servidor ascendente, ntfy.sh por defecto, que se encarga de despertar la app. El ajuste es upstream-base-url: "https://ntfy.sh". Esa petición solo contiene el identificador del mensaje (cabecera X-Poll-ID) y el hash SHA-256 de la URL del topic, nunca el texto: el servidor autoalojado envía literalmente «New message» y la app va a buscar el contenido real a su servidor. Sin upstream-base-url, la documentación advierte de que las notificaciones acaban llegando, pero la entrega puede tardar horas (20 a 30 minutos si el teléfono se usa activamente). upstream-access-token es opcional: la doc solo lo reserva para cuando se superan los límites del servidor ascendente o este está protegido.
Checklist iOS: siete comprobaciones antes de buscar más lejos
base-url— definido con la URL pública en HTTPS de su servidor, la que el teléfono puede alcanzar.upstream-base-url— presente enserver.yml(o como variable de entorno), valorhttps://ntfy.sh, contenedor reiniciado.- Servidor por defecto de la app — en los ajustes de la app iOS, la URL de su servidor es la declarada como servidor por defecto.
- Suscripción al servidor correcto — el topic se añadió eligiendo su servidor, no
ntfy.sh: un topic con el mismo nombre en el servidor público es otro topic. - Token ascendente —
upstream-access-tokensolo sirve si se alcanzan los límites del relé; sin causa establecida, no lo añada por reflejo esperando arreglar el problema. - Registros del servidor — compruebe que el mensaje se publica y luego se transmite al relé. Si es así, su lado funciona y el problema está entre el relé, Apple y el teléfono.
- Ajustes del teléfono — Actualización en segundo plano y Notificaciones activadas para la app, y ningún bloqueador DNS (AdGuard Home, Pi-hole) que filtre
ntfy.sh.
Cuando no llega nada pese a todo: lo que dicen realmente las issues #1305 y #1844
Estas dos issues del repositorio de ntfy describen el síntoma que busca: un servidor autoalojado, un push iOS que no aparece, mensajes que solo aparecen al refrescar la app a mano. Leídas el 30 de octubre de 2026, ambas están abiertas y ninguna señala una causa confirmada.
#1305 «No push notifications on ios?» (abierta en marzo de 2025, etiquetas ios y bug). El autor no recibe ningún aviso, ni con ntfy.sh ni con su propia instancia. Algunos comentarios dicen que reinstalar la app ayudó, otros que el problema volvió meses después. Un colaborador propone que la app se reabone a los topics de Firebase al arrancar y abrió una PR en borrador, precisando que no pudo probarla. Un mantenedor pregunta al autor si la suscripción de Firebase es la causa, sin concluir.
#1844 «No iOS Push notification, also with valid subscription» (abierta en julio de 2026, etiquetas ios, server y bug). El autor ejecuta ntfy en Docker con NTFY_UPSTREAM_BASE_URL en https://ntfy.sh y NTFY_UPSTREAM_ACCESS_TOKEN informado. Dice haber alcanzado el límite gratuito de 250 mensajes al día y haber contratado después una suscripción de pago. Sus registros muestran el mensaje publicado y enviado al relé, y aun así ningún push. Un colaborador responde que los errores 429 pueden suponer un bloqueo de IP de hasta 4 horas, constata que la prueba con curl del autor funciona (por tanto no hay bloqueo) y que la cuenta mostraba 48 mensajes enviados de 2 500. Añade que las notificaciones iOS son caprichosas sin que se sepa exactamente por qué. Solución alternativa comunicada: desactivar y volver a activar «Actualización en segundo plano» y «Notificaciones» en los ajustes de iOS. El autor escribe que no sabe por qué funciona; el problema volvió, luego desapareció sin causa conocida, y otro usuario confirma la solución en septiembre de 2026. Otro informe relaciona un caso parecido con listas de bloqueo DNS.
Lo que se puede concluir y lo que no. Ni un límite diario de mensajes ni un bloqueo 429 quedan establecidos como causa en estos hilos: el 429 se descarta para la IP del autor, y el límite de 250 mensajes es una cifra dada por él, no por la documentación de configuración. El token ascendente no resuelve ahí el problema. Trate la lista anterior como comprobaciones, no como una corrección garantizada.
Tip: no confíe una alerta crítica solo al push de iOS
Mientras estas issues sigan abiertas, duplique las alertas que importan: añada la cabecera X-Email (vea el paso SMTP) para recibir también un correo, o mantenga un segundo canal en el teléfono. Los colaboradores de ambas issues sugieren además la PWA (web push) como alternativa a la app iOS. Y pruebe de extremo a extremo tras cada actualización de la app o de iOS, con el heartbeat del watchdog descrito más arriba.
Integraciones avanzadas: Backrest, Uptime Kuma, Grafana
Backrest soporta ntfy de forma nativa: configure la URL del topic y el token Bearer en la UI de Backrest, y cada snapshot completada o fallida genera automáticamente una notificación push con el nombre de la snapshot, el tamaño deduplicado y la duración. Cero scripts, cero cron.
Uptime Kuma soporta ntfy de forma nativa en su gestor de notificaciones. Cada monitor puede tener múltiples canales — combine ntfy para el push móvil y el SMTP de ntfy para la trazabilidad.
Grafana usa webhooks genéricos. Cree un contact point de tipo webhook, apúntelo a https://ntfy.ejemplo.com/grafana, añada las cabeceras Authorization: Bearer tk_... y Content-Type: text/plain.
Consejo: pruebe la cadena de entrega de extremo a extremo antes de confiar en ella
Antes de conectar su infraestructura a ntfy, pruebe la cadena completa: envíe una notificación con curl, confirme que aparece en la interfaz web, que llega a la app móvil, y que el correo SMTP de respaldo aterriza en la bandeja de entrada. Programe un cron */30 * * * * que envíe un heartbeat al topic watchdog — si la app móvil no lo recibe en 30 minutos, ntfy en sí mismo está caído.