Self-host Healthchecks — чтобы знать, что бэкап молчит

У меня была классическая проблема: бэкап настроен, скрипт по таймеру отрабатывает, и я об этом... никак не узнаю. Пока всё хорошо — тишина. И пока однажды не понадобится восстановиться, ты искренне веришь, что бэкапы делаются. А потом выясняется, что три недели назад скрипт тихо падал на каждом запуске, и свежего архива нет. Классика.

Проблема тут фундаментальная: обычный мониторинг проверяет, что сервис жив. А мне нужно обратное — узнать, что задача НЕ выполнилась. Это называется dead-man's switch: задача должна регулярно подавать признаки жизни, и если она замолчала — это и есть сигнал тревоги. Молчание = проблема.

Для этого поднял у себя Healthchecks. Это сервис, который ждёт от твоей задачи ping по HTTP. Задача успешно отработала — дёрнула URL. Не дёрнула в отведённое окно — Healthchecks сам шлёт алерт. То есть мне не нужно ничего активно опрашивать; если бэкап молча перестал делаться, я об этом узнаю не через месяц, а на следующее же утро.

Стек в Docker, /app/healthchecks/:

services:
  healthchecks:
    image: healthchecks/healthchecks:latest
    container_name: healthchecks
    restart: unless-stopped
    environment:
      - SECRET_KEY=${SECRET_KEY}
      - SITE_ROOT=http://hc.local:8000
      - DEFAULT_FROM_EMAIL=hc@hc.local
      - EMAIL_HOST=${SMTP_HOST}
      - EMAIL_PORT=587
      - EMAIL_HOST_USER=${SMTP_USER}
      - EMAIL_HOST_PASSWORD=${SMTP_PASS}
    volumes:
      - ./data:/data
    ports:
      - "8000:8000"

В вебморде создаёшь check, задаёшь расписание (можно прямо cron-выражением) и grace period — сколько ждать опоздание, прежде чем паниковать. Получаешь уникальный ping-URL.

Дальше прикручиваю его к бэкап-скрипту. Главное — пинговать в самом конце, после set -e, чтобы упавший скрипт не успел отрапортовать об успехе:

#!/bin/bash
set -e

PING_URL="https://hc.local:8000/ping/xxxxxxxx-xxxx-xxxx"

# ... сам бэкап ...
restic backup /app /etc

# дошли сюда без ошибок — рапортуем
curl -fsS -m 10 --retry 3 "$PING_URL" > /dev/null

Можно ещё красивее: пинговать $PING_URL/start в начале и $PING_URL/fail в блоке обработки ошибки — тогда в истории видно длительность задачи и явные падения отдельно от опозданий.

Алерты вешаю на почту, но Healthchecks умеет и в кучу других каналов. Теперь у меня правило простое: если что-то важное должно происходить регулярно — оно дёргает Healthchecks. И отсутствие новостей перестало означать хорошие новости — теперь отсутствие пинга означает, что мне прилетит письмо. Спится спокойнее.