Gotify es un servidor de notificaciones push ligero y autoalojado: los scripts y servicios envían mensajes a una API REST y los clientes (la interfaz web y la aplicación de Android) los reciben al instante por WebSocket, sin pasar por servicios de terceros. En este tutorial desplegarás Gotify con Docker Compose en Ubuntu 24.04, lo publicarás detrás de Nginx con un certificado de Let's Encrypt, crearás una aplicación con su token y enviarás notificaciones desde la línea de comandos y desde cron.

Requisitos previos

Para seguir esta guía necesitas:

  • Un servidor con Ubuntu 24.04 LTS, por ejemplo un VPS de CubePath, y un usuario no root con privilegios sudo. Gotify funciona con 512 MB de RAM.
  • Docker Engine y el plugin Docker Compose instalados.
  • Nginx instalado y los puertos 80 y 443 abiertos. Si usas UFW: sudo ufw allow 'Nginx Full'.
  • Un subdominio (en esta guía, gotify.your_domain) con un registro DNS A que apunte a la IP del servidor.

Paso 1: Preparar el proyecto

Crea un directorio para Gotify:

sudo mkdir -p /opt/gotify
sudo chown "$USER": /opt/gotify
cd /opt/gotify

La contraseña del administrador inicial se pasa como variable de entorno. Guárdala en un archivo .env que solo pueda leer tu usuario, generándola al azar:

echo "GOTIFY_ADMIN_PASS=$(openssl rand -base64 18)" > .env
chmod 600 .env
cat .env

Anota la contraseña: la necesitarás para iniciar sesión.

Paso 2: Desplegar Gotify con Docker Compose

Crea el archivo de Compose:

nano docker-compose.yml
services:
  gotify:
    image: gotify/server:latest
    container_name: gotify
    restart: unless-stopped
    ports:
      - "127.0.0.1:8080:80"
    volumes:
      - ./data:/app/data
    environment:
      GOTIFY_DEFAULTUSER_NAME: admin
      GOTIFY_DEFAULTUSER_PASS: ${GOTIFY_ADMIN_PASS}
      GOTIFY_REGISTRATION: "false"
      TZ: Europe/Madrid

Puntos importantes:

  • El puerto se publica solo en 127.0.0.1, así que Gotify no es accesible desde fuera hasta que Nginx lo publique con HTTPS.
  • ./data guarda la base de datos SQLite, las imágenes de las aplicaciones y los plugins. Es lo único que necesitas copiar para hacer una copia de seguridad.
  • GOTIFY_DEFAULTUSER_* solo se aplica en el primer arranque, cuando la base de datos está vacía. Cambiar después la variable no cambia la contraseña.
  • GOTIFY_REGISTRATION: "false" impide que cualquiera cree una cuenta desde la página de inicio de sesión.

Arranca el contenedor:

docker compose up -d

Comprueba que está en marcha y que responde en local:

docker compose ps
curl -s http://127.0.0.1:8080/health
{"health":"green","database":"green"}

Paso 3: Publicar Gotify con Nginx y HTTPS

Los clientes de Gotify reciben los mensajes por una conexión WebSocket que se mantiene abierta, así que Nginx debe reenviar las cabeceras Upgrade y Connection y no cortar la conexión a los pocos segundos. Crea el sitio:

sudo nano /etc/nginx/sites-available/gotify
server {
    listen 80;
    listen [::]:80;
    server_name gotify.your_domain;

    location / {
        proxy_pass http://127.0.0.1:8080;
        proxy_http_version 1.1;

        proxy_set_header Host $http_host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;

        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "upgrade";

        proxy_connect_timeout 1m;
        proxy_send_timeout 1m;
        proxy_read_timeout 1m;
    }
}

Los clientes de Gotify envían un ping periódico por el WebSocket, por lo que un proxy_read_timeout de un minuto es suficiente para mantener la conexión viva.

Activa el sitio, valida la configuración y recarga Nginx:

sudo ln -s /etc/nginx/sites-available/gotify /etc/nginx/sites-enabled/
sudo nginx -t
sudo systemctl reload nginx

Obtén el certificado con Certbot, que añade la configuración TLS y la redirección a HTTPS:

sudo apt install certbot python3-certbot-nginx
sudo certbot --nginx -d gotify.your_domain

Comprueba el acceso desde fuera:

curl -s https://gotify.your_domain/health
{"health":"green","database":"green"}

Paso 4: Iniciar sesión y crear una aplicación

Gotify separa dos tipos de credenciales:

TipoPara qué sirveDónde se crea
Token de aplicaciónEnviar mensajes. Cada script o servicio emisor tiene su aplicación.Pestaña Apps
Token de clienteLeer mensajes y gestionar el servidor. Lo usan la web y la app móvil.Pestaña Clients (la web y la app de Android lo crean al iniciar sesión)

Abre https://gotify.your_domain e inicia sesión como admin con la contraseña del paso 1. Si quieres cambiarla, hazlo desde el menú de usuario.

Crea una aplicación para las alertas del servidor:

  1. Ve a Apps y pulsa Create Application.
  2. Ponle un nombre, por ejemplo servidor-web01, y una descripción.
  3. Pulsa el icono del ojo junto a la aplicación para ver su token y cópialo.

También puedes crearla con la API usando las credenciales del administrador:

curl -u admin -H "Content-Type: application/json" \
  -d '{"name": "servidor-web01", "description": "Alertas de web01"}' \
  https://gotify.your_domain/application

curl te pedirá la contraseña y la respuesta incluye el token en el campo token:

{"id":1,"token":"AbCdEf123456789","name":"servidor-web01","description":"Alertas de web01","internal":false,"image":"static/defaultapp.png","defaultPriority":0,"lastUsed":null}

Paso 5: Enviar notificaciones con la API

Envía un mensaje con el token de la aplicación en la cabecera X-Gotify-Key (sustituye your_app_token). Mantén abierta la interfaz web mientras lo haces:

curl -H "X-Gotify-Key: your_app_token" \
  -F "title=Prueba" \
  -F "message=Gotify funciona" \
  -F "priority=5" \
  https://gotify.your_domain/message

El mensaje debe aparecer en la web sin recargar la página, lo que confirma que el WebSocket atraviesa Nginx correctamente.

La prioridad es un número de 0 a 10. La aplicación de Android asigna cada mensaje a un canal de notificación según ese valor (0: mínima, 1-3: baja, 4-7: normal, 8-10: alta), y el sonido o la vibración de cada canal se ajustan desde los ajustes de notificaciones de Android.

Con el formato JSON puedes añadir extras. Este mensaje se muestra como Markdown y abre una URL al pulsar la notificación en Android:

curl -H "X-Gotify-Key: your_app_token" -H "Content-Type: application/json" \
  -d '{
    "title": "Despliegue terminado",
    "message": "**v2.4.1** en producción. [Ver el panel](https://grafana.your_domain)",
    "priority": 5,
    "extras": {
      "client::display": {"contentType": "text/markdown"},
      "client::notification": {"click": {"url": "https://grafana.your_domain"}}
    }
  }' \
  https://gotify.your_domain/message

Paso 6: Enviar alertas desde scripts y cron

Construir el JSON a mano en bash falla en cuanto el texto contiene comillas o saltos de línea. Un pequeño script que use jq para montar el cuerpo evita ese problema. Instala jq:

sudo apt install jq

Guarda la URL y el token en un archivo que solo pueda leer root:

sudo nano /etc/gotify-send.env
GOTIFY_URL=https://gotify.your_domain
GOTIFY_TOKEN=your_app_token
sudo chmod 600 /etc/gotify-send.env

Crea el script:

sudo nano /usr/local/bin/gotify-send
#!/usr/bin/env bash
set -euo pipefail

if [[ $# -lt 2 ]]; then
  echo "Uso: gotify-send TITULO MENSAJE [PRIORIDAD]" >&2
  exit 64
fi

# shellcheck source=/dev/null
. /etc/gotify-send.env

payload="$(jq -n --arg title "$1" --arg message "$2" --argjson priority "${3:-5}" \
  '{title: $title, message: $message, priority: $priority}')"

curl -fsS -m 10 --retry 3 -o /dev/null \
  -H "X-Gotify-Key: ${GOTIFY_TOKEN}" \
  -H "Content-Type: application/json" \
  -d "$payload" \
  "${GOTIFY_URL}/message"
sudo chmod 755 /usr/local/bin/gotify-send

Pruébalo con un texto que incluya comillas:

sudo gotify-send "Prueba de script" 'Mensaje con "comillas" y
dos líneas' 4

La notificación debe llegar con el texto intacto. Si el token es incorrecto, curl termina con un error 401 y el script devuelve un código distinto de cero.

Para recibir un aviso cuando falle una tarea de cron, encadena el script con ||. Por ejemplo, en /etc/cron.d/backup:

sudo nano /etc/cron.d/backup
0 3 * * * root /usr/local/bin/backup.sh || /usr/local/bin/gotify-send "Backup fallido en $(hostname)" "backup.sh terminó con error, revisa los logs" 8

Sustituye /usr/local/bin/backup.sh por tu tarea real. El aviso solo se envía si la tarea termina con un código de salida distinto de cero.

Paso 7: Instalar la aplicación de Android

Instala Gotify desde Google Play o F-Droid, introduce https://gotify.your_domain como servidor e inicia sesión con tu usuario. La aplicación crea su propio token de cliente, que verás en la pestaña Clients de la web; si pierdes el móvil, borra ese cliente para revocar su acceso.

Envía otro mensaje de prueba con prioridad 8 y comprueba que llega al teléfono.

Solución de problemas

Los mensajes solo aparecen al recargar la web o no llegan al móvil. El WebSocket no está atravesando el proxy. Revisa que el bloque location de Nginx incluye proxy_http_version 1.1 y las cabeceras Upgrade y Connection, y que no hay otro proxy delante (una CDN, por ejemplo) que corte las conexiones largas.

401 Unauthorized al enviar mensajes. Estás usando un token de cliente en lugar de un token de aplicación, o al revés. Para POST /message se necesita el token de una aplicación.

No puedes iniciar sesión con la contraseña del archivo .env. La base de datos ya existía cuando cambiaste la variable, así que se sigue usando la contraseña anterior. Si es una instalación nueva sin datos que conservar, para el contenedor con docker compose down, borra el directorio data y vuelve a arrancarlo.

Conclusión

Tienes Gotify funcionando con Docker detrás de Nginx con HTTPS, una aplicación con su token para enviar mensajes y un script seguro para lanzar alertas desde tareas programadas. Para actualizarlo, ejecuta docker compose pull y docker compose up -d en /opt/gotify. Como siguientes pasos, puedes conectar Uptime Kuma o Grafana (ambos tienen integración con Gotify), crear una aplicación distinta por servidor para distinguir el origen de cada aviso e incluir el directorio /opt/gotify/data en tus copias de seguridad.