← All articles
MONITORING Healthchecks: Self-Hosted Cron Job and Background Ta... 2026-02-09 · healthchecks · cron · monitoring

Healthchecks: Self-Hosted Cron Job and Background Task Monitoring

Monitoring 2026-02-09 healthchecks cron monitoring alerting

Healthchecks is a cron job monitoring service. The concept is simple: your scripts "ping" Healthchecks when they run successfully. If a ping doesn't arrive on schedule, you get an alert. It catches the silent failures — the backup script that stopped running three weeks ago, the certificate renewal that quietly errored out.

Uptime monitoring tells you when services go down. Healthchecks tells you when scheduled tasks stop working.

Why Self-Host Healthchecks?

The hosted version at healthchecks.io is excellent, but self-hosting gives you:

The trade-off: you're responsible for keeping the monitoring system itself running. Monitoring your monitor is a real consideration — if your server goes down, Healthchecks goes down with it. For critical monitoring, consider the hosted version or run Healthchecks on a separate server.

Installation

# docker-compose.yml
services:
  healthchecks:
    image: healthchecks/healthchecks:latest
    container_name: healthchecks
    ports:
      - "8000:8000"
    volumes:
      - healthchecks_data:/data
    environment:
      - ALLOWED_HOSTS=*
      - DB=sqlite
      - DB_NAME=/data/hc.sqlite
      - DEBUG=False
      - [email protected]
      - EMAIL_HOST=smtp.yourdomain.com
      - EMAIL_HOST_USER=your-smtp-user
      - EMAIL_HOST_PASSWORD=your-smtp-password
      - EMAIL_PORT=587
      - EMAIL_USE_TLS=True
      - PING_BODY_LIMIT=10000
      - REGISTRATION_OPEN=False
      - SECRET_KEY=your-secret-key-here
      - SITE_NAME=My Healthchecks
      - SITE_ROOT=https://healthchecks.yourdomain.com
    restart: unless-stopped

volumes:
  healthchecks_data:

Generate a secret key:

python3 -c "import secrets; print(secrets.token_urlsafe(50))"
docker compose up -d

Create the admin user:

docker exec -it healthchecks ./manage.py createsuperuser

How It Works

The Ping Model

Each check gets a unique ping URL:

https://healthchecks.yourdomain.com/ping/<uuid>

Your cron job hits this URL on success. That's it. If the ping doesn't arrive within the expected schedule + grace period, Healthchecks alerts you.

Adding a Check to a Cron Job

The simplest integration — add a curl to the end of your script:

#!/bin/bash
# backup.sh
borgmatic --verbosity 1 && curl -fsS --retry 3 https://healthchecks.yourdomain.com/ping/<uuid>

The && ensures the ping only fires if the backup succeeds. The -fsS --retry 3 makes curl quiet on success, loud on failure, with retries.

Signal-Based Pings

Healthchecks supports start and failure signals:

#!/bin/bash
PING_URL="https://healthchecks.yourdomain.com/ping/<uuid>"

# Signal start
curl -fsS --retry 3 $PING_URL/start

# Do the work
/usr/bin/my-backup-script

# Signal success or failure
if [ $? -eq 0 ]; then
    curl -fsS --retry 3 $PING_URL
else
    curl -fsS --retry 3 $PING_URL/fail
fi

The /start signal lets Healthchecks measure job duration and detect jobs that start but never finish.

Sending Logs with Pings

Include output in the ping body for debugging failed jobs:

#!/bin/bash
PING_URL="https://healthchecks.yourdomain.com/ping/<uuid>"
OUTPUT=$(/usr/bin/my-script 2>&1)
curl -fsS --retry 3 --data-raw "$OUTPUT" $PING_URL/$?

The $? exit code maps to /0 (success) or /fail (non-zero). The script output is stored and viewable in the Healthchecks UI.

Configuring Checks

Schedule Types

Use cron expressions for scheduled tasks with specific timing. Use simple intervals for tasks that run "roughly every N hours."

Grace Period

The grace period is how long Healthchecks waits after a missed ping before alerting. Set this based on how variable your job's timing is:

Tags

Tag your checks for organization:

Tags are filterable in the dashboard and can be used in notification rules.

Alert Integrations

Healthchecks supports many notification channels:

Email (Built-in)

Configured via the environment variables in docker-compose. Each check can have email recipients.

Webhooks

URL: https://your-webhook-endpoint.com/alerts
POST body (JSON):
{
    "$NAME": check name,
    "$STATUS": "down" or "up",
    "$TAGS": space-separated tags
}

Popular Integrations

What to Monitor

Essential Checks for Self-Hosters

Check Schedule Grace Priority
Backup script 0 2 * * * 2h Critical
Certificate renewal 0 3 1 * * 24h High
Docker image updates 0 6 * * 0 12h Medium
Disk space check */30 * * * * 1h High
Database vacuum 0 4 * * 0 6h Low
DNS record check 0 */6 * * * 12h Medium
SMART disk check 0 5 * * * 24h High

Monitoring Docker Containers

Use Docker healthchecks with a reporting script:

#!/bin/bash
# check-containers.sh
UNHEALTHY=$(docker ps --filter health=unhealthy --format '{{.Names}}' | tr '\n' ', ')
if [ -z "$UNHEALTHY" ]; then
    curl -fsS https://healthchecks.yourdomain.com/ping/<uuid>
else
    curl -fsS --data-raw "Unhealthy: $UNHEALTHY" https://healthchecks.yourdomain.com/ping/<uuid>/fail
fi

Monitoring with Systemd Timers

For systemd-based systems, add an ExecStartPost or OnSuccess handler:

# /etc/systemd/system/backup.service
[Service]
ExecStart=/usr/local/bin/backup.sh
ExecStartPost=/usr/bin/curl -fsS https://healthchecks.yourdomain.com/ping/<uuid>

Healthchecks vs Uptime Kuma vs Gatus

Feature Healthchecks Uptime Kuma Gatus
Focus Cron/task monitoring Service uptime Service uptime
Check model Ping-based (passive) Probe-based (active) Probe-based (active)
Cron monitoring Excellent Basic No
Job duration tracking Yes No No
Log capture Yes (ping body) No No
Status page Basic Beautiful Yes
Setup complexity Low Very low Low
Configuration Web UI Web UI YAML

These tools complement each other:

Verdict

Every self-hoster has experienced the slow realization that a backup script hasn't run in weeks, or that a certificate renewal failed silently. Healthchecks prevents this class of failure with minimal effort — adding a single curl to your scripts.

The self-hosted version is lean, reliable, and easy to maintain. The only real consideration is the meta-monitoring problem: if you run Healthchecks on the same server as your cron jobs, a server failure takes both down. For critical infrastructure, consider running Healthchecks on a separate machine or using the hosted version alongside your self-hosted instance.