KnowledgebaseSelf-Hosting › Self-hosted Healthchecks — monitoring cron jobs and scheduled tasks

Self-hosted Healthchecks — monitoring cron jobs and scheduled tasks

Healthchecks is the "dead man's switch" pattern as a service — your scheduled job pings a URL when it succeeds; if the URL doesn't get pinged within the expected window, you get alerted. Healthchecks.io is the popular hosted version; the open-source server (also called Healthchecks) runs on any VPS. This article covers the self-hosted setup.

The "ping on success" model

The pattern that catches what crontab monitoring usually misses:

# Bad: cron job mails on error, silent on success
0 3 * * * /usr/local/bin/backup.sh

# Better: ping on success, alert if ping doesn't arrive
0 3 * * * /usr/local/bin/backup.sh && curl -fsS -m 10 https://hc.example.com/<uuid>

If backup.sh errors out (non-zero exit), the && short-circuits and the ping doesn't fire. Healthchecks detects the missing ping within the grace window and alerts you.

Install (Docker Compose)

# /opt/healthchecks/docker-compose.yml
services:
  healthchecks:
    image: healthchecks/healthchecks:latest
    container_name: healthchecks
    restart: unless-stopped
    environment:
      DEBUG: "False"
      SECRET_KEY: <long-random-string>
      DB: sqlite                 # use 'postgres' + DATABASE_URL for production
      SITE_ROOT: https://hc.example.com
      SITE_NAME: My Healthchecks
      DEFAULT_FROM_EMAIL: hc@example.com
      EMAIL_HOST: smtp.example.com
      EMAIL_PORT: 587
      EMAIL_HOST_USER: hc@example.com
      EMAIL_HOST_PASSWORD: <smtp-pw>
      EMAIL_USE_TLS: "True"
    volumes:
      - /opt/healthchecks/data:/data
    ports:
      - "127.0.0.1:8000:8000"

Reverse proxy + TLS as usual.

Creating checks

Web UI: New Check, give it a name, set the schedule (cron expression or simple interval), assign tags. Healthchecks generates a unique ping URL. Copy that into your cron job.

For more elaborate "started + completed" tracking:

# Ping start
curl -fsS -m 10 https://hc.example.com/<uuid>/start

# Run job
./backup.sh; rc=$?

# Ping fail or success
if [ $rc -eq 0 ]; then
    curl -fsS -m 10 https://hc.example.com/<uuid>
else
    curl -fsS -m 10 https://hc.example.com/<uuid>/fail
fi

This way you see job duration in the dashboard AND explicit fail events.

Notification channels

Settings → Notifications. Supported: email, SMS (Twilio), Slack, Discord, Telegram, PagerDuty, generic webhook, ntfy, Pushover, Pushbullet. Configure once per channel, attach to checks individually.

Why self-host instead of using healthchecks.io

  • Free tier limits. Healthchecks.io's free tier covers 20 checks; self-hosted has no limit.
  • Data sovereignty. If your checks contain anything sensitive (job names that reveal infrastructure topology, customer identifiers in tags), self-hosting keeps it inside your control.
  • Custom retention. Self-hosted lets you keep more historical data than the hosted tier allows.

The classic "monitor the monitor" problem

If your Healthchecks server goes down, no alerts fire when other things fail. Mitigations:

  • Run Healthchecks on a DIFFERENT VPS than the things it's watching. Cheap insurance.
  • Pair self-hosted Healthchecks with a free Healthchecks.io check that pings YOUR self-hosted instance every 5 minutes. Catches "my monitor is down" externally.

Backups

The SQLite database in /opt/healthchecks/data/ contains all check definitions, ping history, notification config. Stop the container for a clean snapshot, restic it, restart.

Also Read

« « Back

Powered by WHMCompleteSolution