Using Uptime Kuma push monitors

Uptime Kuma has a “push” monitor type which supports sending in status updates for something like a cron job to make sure it continues to execute. I use this for tasks like running backups or cleanup chores.

Aside: To avoid tying monitoring to my normal infrastructure, I’ve been successfully running Uptime Kuma on fly.io. I also recommend Cronitor for a similar commercial offering but its pricing doesn’t align well with my personal projects.

Normally to update Uptime Kuma you might append && curl … to invoke the monitor URL at the end of a script. I think this is a little too limiting, so I’ve written a small script that wraps an underlying command with a monitor update:

#!/bin/bash
if [ $# -lt 1 ]; then
    echo "Usage: $0 <push_token> [command...]" >&2
    exit 1
fi

push_token=$1; shift
start_time=$(date -u +%s%3N)

if [ $# -gt 0 ]; then
    "$@" || exit
fi

end_time=$(date -u +%s%3N)
duration=$(($end_time - $start_time))

result=$(curl --fail --no-progress-meter --retry 3 "https://uptimekuma.example.com/api/push/$push_token?ping=$duration" 2>&1)
if [ $? -ne 0 ]; then
    echo "Failed: $result" >&2
fi

This script is invoked with the token and an optional underlying command to execute and report to Uptime Kuma when the command is successful along with its execution time as the ping property. For example:

kuma 3EpwDA93fC docker system prune -a -f

I’ve automated this using the ansible-uptime-kuma Ansible collection to automatically create “push” monitors for recurring jobs and “http” monitors for web-facing services. This ends up looking something like the following for a push monitor:

- name: Store the monitor name
  set_fact:
    monitor_name: "{{ inventory_hostname }}-borgmatic"
- name: Create Uptime Kuma push monitor
  delegate_to: 127.0.0.1
  lucasheld.uptime_kuma.monitor:
    api_url: "https://uptimekuma.example.com"
    api_token: "{{ uptime_kuma_api_token }}"
    type: push
    name: "{{ monitor_name }}"
    interval: 3600
- name: Get Uptime Kuma push monitor info
  delegate_to: 127.0.0.1
  lucasheld.uptime_kuma.monitor_info:
    api_url: "https://uptimekuma.example.com"
    api_token: "{{ uptime_kuma_api_token }}"
    name: "{{ monitor_name }}"
  register: monitor_info
- name: Set Uptime Kuma push token
  set_fact:
    push_token: "{{ monitor_info.monitors[0].pushToken }}"
- name: Create borg cronjob
  cron:
    name: "Borg backups"
    job: "/usr/local/bin/kuma {{ push_token }} /usr/local/bin/borgmatic create"
    minute: 33

This creates a monitor for my borgmatic cron and executes it hourly. When it fails to check in, Uptime Kuma sends me notifications, and when it succeeds it keeps track of how long it takes to execute. Perfect!