Help CenterOpen app

Monitores Heartbeat

Los monitores Heartbeat invierten la dirección: tu tarea nos dice que está viva. Dos modos: Programado (intervalo esperado) y Receptor de eventos (inactivo; se dispara solo cuando los disparadores coinciden con el payload).

MODOS

  • Programado: si no llega ningún latido dentro del intervalo configurado, el estado pasa a MISS y se disparan las alertas.
  • Fallo explícito programado: llama a la misma URL con ?status=fail cuando la tarea se ejecutó pero detectó un mal resultado. Esto registra un latido KO y activa el manejo de caída inmediatamente.
  • Receptor de eventos: el estado permanece inactivo. Cada latido almacena su payload; los disparadores con condiciones lo evalúan. ?status=fail se ignora en el modo Receptor de eventos porque las condiciones de los disparadores deciden qué importa.
Endpoints de heartbeat programado
OK beat:
curl -fsS https://keepitalive.dev/heartbeat/{token}

Explicit failed beat:
curl -fsS "https://keepitalive.dev/heartbeat/{token}?status=fail"

RECETAS PROGRAMADAS

Heartbeat cron cada 5 minutos
*/5 * * * * curl -fsS https://keepitalive.dev/heartbeat/{token} >/dev/null
Heartbeat tras el éxito de una tarea
0 2 * * * /usr/bin/backup.sh && curl -fsS https://keepitalive.dev/heartbeat/{token}
Informar fallo explícitamente
curl -fsS "https://keepitalive.dev/heartbeat/{token}?status=fail&payload=disk+full"
Relé aislado: hacer ping a un host privado desde una gateway accesible
*/1 * * * * if ping -c 1 -W 2 192.168.1.50 >/dev/null 2>&1; then \
  curl -fsS "https://keepitalive.dev/heartbeat/{token}"; \
else \
  curl -fsS -X POST "https://keepitalive.dev/heartbeat/{token}?status=fail" \
    -d "unreachable"; \
fi
Programador de tareas de Windows (.bat)
:: C:\scripts\keepalive_heartbeat.bat
@echo off
curl -fsS https://keepitalive.dev/heartbeat/{token} >nul 2>&1

:: schtasks /create /tn "KEEPitALIVE" /tr C:\scripts\keepalive_heartbeat.bat /sc minute /mo 5

PROGRAMACIONES CRON

Un heartbeat simple espera una senal cada N segundos, lo que no encaja con un trabajo que corre por calendario: una copia de seguridad solo en dias laborables parece llevar 65 horas de retraso cada domingo. Define una expresion cron y el plazo pasa a ser la proxima ejecucion programada mas la tolerancia: nada llega tarde hasta que algo se esperaba. Elige la zona horaria en la que corre el trabajo: la programacion se evalua ahi, y eso mantiene un trabajo de las 03:00 a las 03:00 cuando cambia la hora. La expresion tambien fija el coste, porque determina cuantas ejecuciones por hora esperamos. La URL de ingesta no cambia; solo cambia cuando esperamos la senal.

Alinear el monitor con el crontab
# Give the monitor the same schedule the job runs on.
0 3 * * 1-5      # 03:00, weekdays only
*/15 * * * *     # every 15 minutes
0 3 1 * *        # 03:00 on the 1st of the month
@daily           # midnight

# Pick the zone the job runs in, not the server's: that is
# what keeps 03:00 at 03:00 when the clocks change.

CÓDIGOS DE SALIDA

Añade el código de salida de tu tarea a la URL y el beat se informa solo: 0 es éxito, cualquier otro valor es un fallo. Es mejor que encadenar con && , que no envía nada cuando la tarea falla, de modo que el monitor solo cae más tarde, al perderse la ventana, en lugar de en el momento de la avería. El código se guarda en el beat, así que una condición de disparador puede enrutar según el tipo de fallo: payload.exit_code == 137 (terminado por memoria) puede abrir una interrupción mientras payload.exit_code == 1 solo notifica. Los códigos deben estar entre 0 y 255; si no tienes un código a mano, también puedes usar /fail.

Informar del código de salida
# $? is the exit status of the job that just ran
0 2 * * * /usr/bin/backup.sh; curl -fsS https://keepitalive.dev/heartbeat/{token}/$?

# Send the job output too, so the alert says why it failed.
# Capture it first: in a pipeline $? is the last command's status,
# not the job's.
0 2 * * * out=$(/usr/bin/backup.sh 2>&1); \
  curl -fsS --data-binary "$out" https://keepitalive.dev/heartbeat/{token}/$?

DURACIÓN DE LA EJECUCIÓN

Haz ping a /start cuando el trabajo empieza y envía una señal normal al terminar, y el tiempo transcurrido se guarda en la señal como duration_ms, de modo que una condición puede alertar de una ejecución lenta - payload.duration_ms > 300000 detecta una copia que tardó más de cinco minutos aunque acabara bien. /start es totalmente opcional: una sola señal final ya es un informe completo, y la detección de fallos y los códigos de salida funcionan sin él. Enviarlo aporta exactamente dos cosas: la duración registrada y la detección de una ejecución que nunca termina. Un /start por sí solo no informa de nada ni cambia el estado del monitor: un trabajo que arranca y se cuelga debe seguir incumpliendo su ventana. Un segundo /start mientras hay una ejecución abierta se rechaza, porque dos inicios sin un final entre ellos significan que el primero nunca informó; el rechazo se levanta cuando esa ejecución excede su plazo. Las ejecuciones abiertas más de 24 horas se descartan.

Cronometrar una tarea
# /start is optional. Without it you still get miss detection
# and the exit status - just no duration and no stuck-run alert:
0 2 * * * /usr/bin/backup.sh; curl -fsS https://keepitalive.dev/heartbeat/{token}/$?

# With it, the run is timed and a job that never finishes is caught:
0 2 * * * curl -fsS https://keepitalive.dev/heartbeat/{token}/start; \
  /usr/bin/backup.sh; \
  curl -fsS https://keepitalive.dev/heartbeat/{token}/$?

EJECUCIONES ATASCADAS

Si una tarea llama a /start y luego se cuelga, todavía no hay nada retrasado: el inicio no refrescó nada, así que el monitor parece correcto hasta que tocara el siguiente beat. Ese hueco es justo el problema de cronometrar una tarea: una ejecución atascada es precisamente el fallo que querías detectar. Por eso una ejecución que no ha enviado su beat final cae en cuanto supera el intervalo más la ventana de gracia, con un error que indica cuánto lleva ejecutándose. No hay un ajuste aparte de duración máxima: un beat retrasado y una ejecución atascada son la misma pregunta — cuánto esperamos antes de darla por perdida — así que la gracia que ya configuraste responde a ambas.

PAYLOADS

Haz POST de JSON a tu URL de heartbeat para informar métricas del sistema o campos arbitrarios. La tarjeta Último Informe en la página de detalle del monitor muestra el último payload como JSON con formato cuando es posible, con texto sin formato como alternativa.

Payload JSON simple
curl -fsS -X POST https://keepitalive.dev/heartbeat/{token} \
  -H 'Content-Type: application/json' \
  -d '{"job":"nightly-backup","status":"ok","rows":42850}'

RECEPTOR DE EVENTOS: MANTENIMIENTO CI/CD

El modo Receptor de eventos es útil para pipelines de despliegue. Crea un disparador de payload con la condición payload.status == "maintenance" y clasificación de incidente maintenance. Envía un payload de mantenimiento antes del despliegue y un payload running/ok en un paso final if: always(). El incidente se abre mientras la expresión coincide y se cierra cuando el siguiente payload aceptado lo limpia.

Ciclo de mantenimiento de GitHub Actions
- name: Start maintenance
  run: |
    curl -fsS -X POST "$KEEPITALIVE_HEARTBEAT_URL" \
      -H "Content-Type: application/json" \
      -d '{"status":"maintenance","source":"github-actions","sha":"${{github.sha}}"}'

- name: Deploy
  run: ./deploy.sh

- name: End maintenance
  if: always()
  run: |
    curl -fsS -X POST "$KEEPITALIVE_HEARTBEAT_URL" \
      -H "Content-Type: application/json" \
      -d '{"status":"running","source":"github-actions","sha":"${{github.sha}}"}'

Los receptores de eventos limitan los payloads aceptados según el intervalo del monitor. Para trabajos de despliegue muy cortos, usa el menor intervalo que permita tu plan o retrasa el payload final hasta que haya transcurrido el intervalo. Si la fuente se queda en silencio, KEEPitALIVE no asume la recuperación.

Linux / macOS - métricas del sistema
#!/bin/bash
URL="https://keepitalive.dev/heartbeat/{token}"
CPU=$(top -bn1 | grep "Cpu" | awk '{print $2}')
RAM=$(free -m | awk '/Mem/{printf "%.1f", $3/$2*100}')
DISK=$(df -h / | awk 'NR==2{print $5}')
curl -fsS -X POST $URL \
  -H "Content-Type: application/json" \
  -d "{\"hostname\":\"$(hostname)\",\"cpu\":\"$CPU%\",\"ram\":\"$RAM%\",\"disk\":\"$DISK\"}"
Windows PowerShell - métricas del sistema
$url = "https://keepitalive.dev/heartbeat/{token}"
$os  = Get-CimInstance Win32_OperatingSystem
$body = @{
  hostname = $env:COMPUTERNAME
  cpu      = "{0:N1}%" -f (Get-CimInstance Win32_Processor).LoadPercentage
  ram      = "{0:N1}%" -f ((1 - $os.FreePhysicalMemory / $os.TotalVisibleMemorySize) * 100)
} | ConvertTo-Json
Invoke-RestMethod -Uri $url -Method POST -Body $body -ContentType "application/json"
SiguienteNotificaciones