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
Powered by WHMCompleteSolution