Gatus es un monitor de disponibilidad ligero que comprueba periódicamente endpoints HTTP, TCP, DNS o ICMP, evalúa condiciones sobre la respuesta (código, tiempo, contenido, caducidad del certificado) y muestra el resultado en una página de estado. Toda la configuración vive en un único archivo YAML, lo que facilita versionarla en Git. En este tutorial instalarás Gatus con Docker Compose en Ubuntu 24.04, definirás varios tipos de checks, activarás alertas en Slack y publicarás la página de estado en tu dominio con HTTPS.

Requisitos previos

  • Un servidor con Ubuntu 24.04 LTS, por ejemplo un VPS de CubePath. Gatus consume muy pocos recursos: 1 vCPU y 1 GB de RAM son suficientes.
  • Un usuario no root con privilegios sudo.
  • Docker Engine y el plugin de Docker Compose instalados desde el repositorio oficial de Docker.
  • Un dominio con un registro DNS A (por ejemplo status.your_domain) apuntando a la IP del servidor, para el paso de HTTPS.
  • Opcional: un webhook entrante de Slack para las alertas.

Paso 1: Crear la estructura del proyecto

Gatus necesita dos cosas: el archivo de configuración y un directorio donde guardar el historial en SQLite, para que no se pierda al recrear el contenedor. Crea el directorio del proyecto:

sudo mkdir -p /opt/gatus/config /opt/gatus/data
sudo chown -R "$USER":"$USER" /opt/gatus
cd /opt/gatus

Paso 2: Escribir una configuración mínima

Empieza con un único endpoint para comprobar que todo funciona. Crea el archivo:

nano /opt/gatus/config/config.yaml
storage:
  type: sqlite
  path: /data/data.db

endpoints:
  - name: Sitio web
    group: Público
    url: "https://your_domain"
    interval: 1m
    conditions:
      - "[STATUS] == 200"
      - "[RESPONSE_TIME] < 1500"
      - "[CERTIFICATE_EXPIRATION] > 168h"

Este endpoint se comprueba cada minuto y se considera sano si responde con 200, en menos de 1,5 segundos y con un certificado TLS que no caduca en los próximos 7 días (168 horas). storage guarda el historial en /data/data.db dentro del contenedor, que montarás desde /opt/gatus/data.

Paso 3: Arrancar Gatus con Docker Compose

Crea el archivo de Compose:

nano /opt/gatus/compose.yaml
services:
  gatus:
    image: twinproduction/gatus:latest
    container_name: gatus
    restart: unless-stopped
    ports:
      - "127.0.0.1:8080:8080"
    env_file: .env
    volumes:
      - ./config:/config:ro
      - ./data:/data

El puerto se publica solo en 127.0.0.1: Docker se salta las reglas de UFW al publicar puertos, así que es la forma segura de que Gatus no quede expuesto directamente. El acceso público se hará a través de Nginx en el paso 7. Para producción, fija una versión concreta en lugar de latest (consulta las etiquetas en github.com/TwiN/gatus/releases).

Crea el archivo .env, de momento vacío; en el paso 5 guardarás ahí el webhook de Slack:

touch /opt/gatus/.env
chmod 600 /opt/gatus/.env

Arranca el contenedor:

docker compose up -d
docker compose logs --tail 20 gatus

En los logs verás que Gatus ha cargado la configuración y empieza a evaluar el endpoint Sitio web. Comprueba que responde:

curl -s http://127.0.0.1:8080/health
{"status":"UP"}

Consulta el resultado del check a través de su API:

curl -s http://127.0.0.1:8080/api/v1/endpoints/statuses | python3 -m json.tool | grep -E '"name"|"success"' | head
        "name": "Sitio web",
                "success": true,

Paso 4: Añadir checks de distintos tipos

Gatus decide el tipo de check por el esquema de la URL. Abre de nuevo la configuración:

nano /opt/gatus/config/config.yaml

Sustituye la sección endpoints por esta, adaptando los nombres de host a tus servicios:

endpoints:
  - name: Sitio web
    group: Público
    url: "https://your_domain"
    interval: 1m
    conditions:
      - "[STATUS] == 200"
      - "[RESPONSE_TIME] < 1500"
      - "[CERTIFICATE_EXPIRATION] > 168h"

  - name: API
    group: Público
    url: "https://api.your_domain/health"
    interval: 30s
    conditions:
      - "[STATUS] == 200"
      - "[BODY].status == ok"
      - "[RESPONSE_TIME] < 500"

  - name: Página de login
    group: Público
    url: "https://app.your_domain/login"
    interval: 2m
    conditions:
      - "[STATUS] == 200"
      - "[BODY] == pat(*Iniciar sesión*)"

  - name: PostgreSQL
    group: Interno
    url: "tcp://10.0.0.5:5432"
    interval: 30s
    conditions:
      - "[CONNECTED] == true"

  - name: Resolución DNS
    group: Interno
    url: "1.1.1.1"
    dns:
      query-name: "your_domain"
      query-type: "A"
    interval: 5m
    conditions:
      - "[DNS_RCODE] == NOERROR"
      - "[BODY] == your_server_ip"

Qué comprueba cada uno:

EndpointTipoCondiciones
Sitio webHTTPCódigo 200, tiempo de respuesta y caducidad del certificado
APIHTTPAdemás, el campo status del JSON devuelto debe valer ok
Página de loginHTTPEl HTML debe contener el texto Iniciar sesión (pat() admite comodines *)
PostgreSQLTCPEl puerto acepta conexiones
Resolución DNSDNSEl servidor 1.1.1.1 resuelve your_domain a la IP esperada

group agrupa los endpoints en la página de estado. Otros placeholders útiles son [IP] (IP resuelta del host) y len([BODY].items) > 0 para comprobar que un array no está vacío.

Gatus detecta los cambios del archivo, pero reiniciar el contenedor tras cada edición es la forma más fiable de aplicarlos y ver los errores al momento:

docker compose restart gatus
docker compose logs --tail 20 gatus

Si hay un error de sintaxis, el contenedor se detiene y el log indica la línea o la clave con problemas.

Paso 5: Configurar alertas en Slack

Las alertas se definen en dos niveles: el proveedor en la sección global alerting, y en cada endpoint la lista de alertas que usa. Guarda la URL del webhook de Slack en .env para no dejarla en el YAML:

nano /opt/gatus/.env
SLACK_WEBHOOK_URL=https://hooks.slack.com/services/your/webhook/url

Gatus sustituye las variables de entorno con la sintaxis ${VARIABLE} en su configuración. Añade al principio de config.yaml:

alerting:
  slack:
    webhook-url: "${SLACK_WEBHOOK_URL}"
    default-alert:
      send-on-resolved: true
      failure-threshold: 3
      success-threshold: 2

Con esta configuración, un endpoint dispara la alerta tras 3 fallos consecutivos, la da por resuelta tras 2 éxitos consecutivos y avisa también de la resolución. Activa la alerta en cada endpoint que quieras vigilar, añadiendo el bloque alerts:

  - name: API
    group: Público
    url: "https://api.your_domain/health"
    interval: 30s
    conditions:
      - "[STATUS] == 200"
      - "[BODY].status == ok"
      - "[RESPONSE_TIME] < 500"
    alerts:
      - type: slack
        description: "La API no responde correctamente"

Cada alerta puede sobrescribir los valores de default-alert, por ejemplo failure-threshold: 5 en un servicio con picos de latencia ocasionales. Gatus soporta también otros proveedores en la misma sección alerting, como email, telegram, discord, pagerduty o custom (un webhook genérico), con claves propias de cada uno.

Aplica los cambios con --force-recreate, necesario para que el contenedor lea el nuevo .env:

docker compose up -d --force-recreate

Para probar la alerta, cambia temporalmente la URL de un endpoint a una ruta que devuelva 404, reinicia Gatus y espera los tres intervalos: recibirás el mensaje en Slack. Restaura la URL y recibirás el aviso de resolución.

Paso 6: Personalizar la página de estado

La página de estado muestra todos los endpoints con su historial reciente. Personaliza el título y los enlaces añadiendo una sección ui en config.yaml:

ui:
  title: "Estado de los servicios"
  header: "Estado de los servicios"
  description: "Disponibilidad en tiempo real de nuestros servicios"
  link: "https://your_domain"
  buttons:
    - name: "Web"
      link: "https://your_domain"
    - name: "Soporte"
      link: "https://your_domain/soporte"

Si algún endpoint es interno y no debe aparecer públicamente, no lo pongas en esta instancia: o usas una instancia distinta para la página pública, o proteges la página con autenticación en el proxy inverso.

Activa también las métricas de Prometheus añadiendo esta línea al nivel superior del archivo:

metrics: true

Reinicia y comprueba que el endpoint /metrics publica series de Gatus:

docker compose restart gatus
curl -s http://127.0.0.1:8080/metrics | grep -m3 '^gatus_results_total'

Paso 7: Publicar la página con Nginx y HTTPS

Instala Nginx y Certbot:

sudo apt update
sudo apt install nginx certbot python3-certbot-nginx

Abre los puertos web en el firewall:

sudo ufw allow 'Nginx Full'

Crea el bloque de servidor:

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

    location / {
        proxy_pass http://127.0.0.1:8080;
        proxy_set_header Host $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;
    }
}

Actívalo, valida la sintaxis y recarga Nginx:

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

Obtén el certificado. Certbot modificará el bloque para servir HTTPS y redirigir el tráfico HTTP:

sudo certbot --nginx -d status.your_domain

Abre https://status.your_domain en el navegador: verás la página de estado con los grupos Público e Interno y la barra de historial de cada endpoint.

Solución de problemas

El contenedor se reinicia en bucle. Casi siempre es un error en config.yaml: indentación, una clave desconocida o una condición mal escrita. Revisa docker compose logs --tail 50 gatus; el mensaje indica qué parte no se ha podido interpretar.

Un endpoint aparece siempre en rojo. Haz clic en el endpoint en la página de estado para ver el detalle de cada comprobación y qué condición ha fallado. Si el servicio es interno, comprueba que el contenedor puede alcanzarlo: los checks salen desde la red de Docker, no desde el host.

Las alertas no llegan a Slack. Comprueba que la variable está en el contenedor con docker compose exec gatus env | grep SLACK (si no aparece, recrea con --force-recreate) y que el endpoint tiene el bloque alerts con type: slack. Recuerda que la alerta solo salta tras failure-threshold fallos seguidos.

Los checks ICMP (icmp://) fallan siempre. El ping necesita sockets raw, que el contenedor puede no tener permitidos. Usa un check TCP a un puerto conocido del host como alternativa.

Conclusión

Has desplegado Gatus con Docker Compose, has definido checks HTTP, TCP y DNS con condiciones sobre la respuesta, has configurado alertas en Slack y has publicado la página de estado con HTTPS. Como siguientes pasos, guarda config.yaml en un repositorio Git para revisar los cambios, añade Gatus como target en Prometheus usando /metrics y crea una segunda instancia en otra ubicación para detectar también las caídas del propio servidor de monitorización.