Heartbeat Monitoring #

Complete guide to passive heartbeat monitoring for scheduled tasks, cron jobs, and background processes.

Overview #

Heartbeat monitoring is a passive monitoring approach where your applications "ping" a unique URL at regular intervals. If a ping is missed, an alert is triggered. This is ideal for monitoring scheduled tasks, cron jobs, backup processes, and other periodic jobs.

Key Differences: Servers vs Heartbeats #

Feature Servers (Active) Heartbeats (Passive)
Direction We check your endpoint Your system pings us
Use Case Websites, APIs Cron jobs, scheduled tasks
Detection Response status codes Missing pings
Setup Add URL to monitor Add ping to your task

Creating a Heartbeat #

1. Navigate to Heartbeats #

Go to Heartbeats in the admin menu.

2. Create New Heartbeat #

Click New Heartbeat and fill in:

  • Name: Descriptive name (e.g., "Daily Database Backup")
  • Timeframe: Expected interval between pings (e.g., "1 hour")
  • Grace Period: Additional time before alerting (optional)

3. Get Your Unique URL #

After saving, you'll receive a unique ping URL:

https://yoursite.com/heartbeat/ping/abc123-def456-ghi789

This URL is permanent and unique to this heartbeat.

4. Add Ping to Your Task #

Add the ping URL to your scheduled task (see integration examples below).

Timeframe Options #

Timeframe Description Use Case
10m Every 10 minutes Frequent checks, real-time processing
30m Every 30 minutes Queue processors, data sync
1h Every hour Hourly reports, cache refresh
1d Every day (24h) Daily backups, daily reports
1w Every week Weekly maintenance, weekly reports
1m Every month Monthly billing, monthly cleanup

Status Indicators #

Status Description
Healthy 🟢 Ping received within expected timeframe
Missing 🔴 No ping received, alert triggered
Recovering 🟡 Ping received after being missing
Never Pinged New heartbeat, waiting for first ping

Integration Examples #

Shell Script / Cron Job #

#!/bin/bash
# backup.sh - Daily backup script

# Your backup logic here
mysqldump mydb > backup.sql
gzip backup.sql

# Send heartbeat ping at the end
curl -fsS --retry 3 https://yoursite.com/heartbeat/ping/abc123-def456

Add to crontab:

0 2 * * * /path/to/backup.sh

PHP Script #

<?php
// scheduled-task.php

// Your task logic here
processQueue();
generateReports();

// Send heartbeat ping
$ch = curl_init('https://yoursite.com/heartbeat/ping/abc123-def456');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_TIMEOUT, 10);
curl_exec($ch);
curl_close($ch);

Python Script #

#!/usr/bin/env python3
import requests

def main():
    # Your task logic here
    process_data()
    send_reports()

    # Send heartbeat ping
    try:
        requests.get(
            'https://yoursite.com/heartbeat/ping/abc123-def456',
            timeout=10
        )
    except requests.RequestException:
        pass  # Don't let ping failure break the script

if __name__ == '__main__':
    main()

Node.js #

const https = require('https');

async function runTask() {
  // Your task logic here
  await processQueue();
  await generateReports();

  // Send heartbeat ping
  https.get('https://yoursite.com/heartbeat/ping/abc123-def456');
}

runTask().catch(console.error);

Laravel Scheduled Task #

// app/Console/Kernel.php

protected function schedule(Schedule $schedule)
{
    $schedule->command('backup:run')
        ->daily()
        ->after(function () {
            Http::get('https://yoursite.com/heartbeat/ping/abc123-def456');
        });
}

Docker Container #

# Dockerfile
CMD ["/bin/sh", "-c", "your-task.sh && curl -fsS https://yoursite.com/heartbeat/ping/abc123-def456"]

GitHub Actions #

# .github/workflows/scheduled.yml
name: Scheduled Task

on:
  schedule:
    - cron: '0 * * * *'  # Every hour

jobs:
  run:
    runs-on: ubuntu-latest
    steps:
      - name: Run task
        run: |
          # Your task here
          echo "Running scheduled task..."

      - name: Send heartbeat
        run: curl -fsS https://yoursite.com/heartbeat/ping/abc123-def456

AWS Lambda #

import urllib.request

def lambda_handler(event, context):
    # Your task logic here
    result = process_event(event)

    # Send heartbeat ping
    try:
        urllib.request.urlopen(
            'https://yoursite.com/heartbeat/ping/abc123-def456',
            timeout=10
        )
    except:
        pass

    return result

Ping URL Options #

Basic Ping #

curl https://yoursite.com/heartbeat/ping/abc123-def456

Ping with Exit Code #

Report success or failure based on your task:

# Report success
curl https://yoursite.com/heartbeat/ping/abc123-def456?status=success

# Report failure
curl https://yoursite.com/heartbeat/ping/abc123-def456?status=failure

# Report with exit code
curl https://yoursite.com/heartbeat/ping/abc123-def456?exit_code=$?

Ping with Timing #

Track execution duration:

start_time=$(date +%s)
# Your task here
end_time=$(date +%s)
duration=$((end_time - start_time))

curl "https://yoursite.com/heartbeat/ping/abc123-def456?duration=${duration}"

Ping with Message #

Include a message with the ping:

curl -X POST https://yoursite.com/heartbeat/ping/abc123-def456 \
  -d "message=Processed 1234 records"

Bulk Import #

CSV Format #

name,timeframe
Daily Backup,1d
Queue Processor,10m
Weekly Report,1w
Monthly Cleanup,1m

Import Process #

  1. Navigate to Heartbeats
  2. Click Import
  3. Upload CSV file
  4. Review imported heartbeats
  5. Copy ping URLs for each heartbeat

Heartbeat Dashboard #

The dashboard shows:

  • Total Heartbeats: Number of configured heartbeats
  • Healthy: Heartbeats receiving pings on time
  • Missing: Heartbeats that missed expected pings
  • Last Ping: When each heartbeat was last pinged

Notifications #

When Alerts Trigger #

Notifications are sent when:

  • A heartbeat goes from healthy to missing
  • A missing heartbeat recovers (first ping after missing)

Notification Content #

💔 Heartbeat Missing: Daily Database Backup
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
Expected: Every 1 day
Last Ping: 26 hours ago
Missing Since: 2 hours ago
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

Plan Limits #

Heartbeats share limits with servers:

Plan Servers + Heartbeats
Free 50 combined
Basic 200 combined
Pro 500 combined
Enterprise Unlimited

Data Retention #

Ping History #

  • Last 1000 pings per heartbeat are retained
  • Older pings are automatically cleaned up
  • Cleanup happens during each new ping

Log Retention #

Status change logs follow the same retention as servers:

  • Free: 7 days
  • Basic: 30 days
  • Pro/Enterprise: 90 days

Best Practices #

Ping at Task Completion #

# Good - ping after task succeeds
backup.sh && curl https://...

# Bad - ping before task runs
curl https://... && backup.sh

Handle Ping Failures Gracefully #

# Don't let ping failure break your task
curl -fsS --retry 3 https://... || true

Use Appropriate Timeframes #

  • Set timeframe slightly longer than task duration
  • Account for network delays
  • Include grace period for variable-length tasks

Include Error Handling #

#!/bin/bash
if backup.sh; then
  curl "https://...?status=success"
else
  curl "https://...?status=failure&message=Backup%20failed"
  exit 1
fi

Troubleshooting #

Heartbeat Always Missing #

Check ping URL:

# Test manually
curl -v https://yoursite.com/heartbeat/ping/abc123-def456

Verify task is running:

  • Check cron logs
  • Verify task execution
  • Check for errors in task output

False Positives #

Increase Timeframe:

  • If task sometimes takes longer than expected
  • Add buffer time for variable execution

Add Grace Period:

  • Accounts for network delays
  • Provides additional buffer

Ping Not Recorded #

Check Network:

  • Firewall rules allowing outbound HTTPS
  • DNS resolution working
  • Verify endpoint is accessible

Check URL:

  • URL is correct and complete
  • No typos in the unique ID
  • HTTPS protocol used

Previous: Server Management | Next: Status Pages