← Blog
Developer Guide 11 min read

How to Monitor Cron Jobs and Background Tasks

Cron jobs fail silently. HTTP monitors can't watch them. Heartbeat monitoring is the right tool — here's how to set it up in Node.js, Python, Go, and shell scripts, with code examples for each.

Uptime monitoring checks that your HTTP endpoints respond. But a large class of critical jobs have no HTTP endpoint to check — they run on a schedule, do their work, and exit. If they stop running, nothing alerts you.

The classic failure mode: a nightly database backup job that silently stopped running three weeks ago. You find out when you need to restore from backup and discover the most recent one is three weeks old.

Heartbeat monitoring inverts the check. Instead of an external service pinging your endpoint to see if it's up, your job pings an external service to say "I ran successfully." If the ping doesn't arrive within the expected window, you get an alert.


How heartbeat monitoring works

The setup has three parts:

  1. You create a heartbeat monitor in PingBase with a schedule (e.g., "every 1 hour") and a grace period (e.g., "5 minutes late is OK").
  2. PingBase gives you a unique URL for that monitor — your ping URL.
  3. You add a single HTTP request to the end of your job that hits that URL when the job completes successfully.

If the ping doesn't arrive within the schedule plus grace period, PingBase marks the monitor as down and sends you an alert through your configured channels.

The ping URL looks like: https://app.pingba.se/hb/your-unique-token


Shell scripts and cron jobs

The simplest case. Add a curl call at the end of your script, after the main work completes successfully:

# backup.sh

#!/bin/bash
set -e

# Your backup logic
pg_dump mydb | gzip > /backups/mydb-$(date +%Y%m%d).sql.gz
aws s3 cp /backups/mydb-$(date +%Y%m%d).sql.gz s3://my-backups/

# Ping PingBase on success
curl -fsS --retry 3 https://app.pingba.se/hb/YOUR_TOKEN > /dev/null

Key flags: -f fails on HTTP errors, -s suppresses progress output, -S still shows errors, --retry 3 retries on transient network failures. The > /dev/null discards the response body.

The set -e at the top ensures the script exits immediately if any command fails — so the ping at the bottom only runs if everything before it succeeded.

For your crontab:

# crontab -e

0 2 * * * /usr/local/bin/backup.sh >> /var/log/backup.log 2>&1

Node.js

For a Node.js worker or scheduled task, add the ping at the end of your job function:

# worker.js

const HEARTBEAT_URL = process.env.PINGBASE_HEARTBEAT_URL;

async function runJob() {
  // Your job logic
  await processQueue();
  await sendReports();

  // Ping PingBase on success
  if (HEARTBEAT_URL) {
    try {
      await fetch(HEARTBEAT_URL);
    } catch (err) {
      // Don't fail the job if the ping fails
      console.warn('Heartbeat ping failed:', err.message);
    }
  }
}

runJob().catch(err => {
  console.error('Job failed:', err);
  process.exit(1);
});

Store the heartbeat URL in an environment variable — don't hardcode it. This makes it easy to disable in development and rotate the token if needed.

For jobs using a scheduler like node-cron:

import cron from 'node-cron';

cron.schedule('0 * * * *', async () => {
  try {
    await processHourlyJob();
    // Ping on success
    await fetch(process.env.PINGBASE_HEARTBEAT_URL);
  } catch (err) {
    console.error('Job failed:', err);
    // Don't ping — missing ping = alert
  }
});

Python

Python's standard library includes urllib, but requests is simpler for this use case:

# job.py

import os
import requests

HEARTBEAT_URL = os.environ.get('PINGBASE_HEARTBEAT_URL')

def ping_heartbeat():
    if not HEARTBEAT_URL:
        return
    try:
        requests.get(HEARTBEAT_URL, timeout=10)
    except Exception as e:
        print(f"Heartbeat ping failed: {e}")

def run_job():
    # Your job logic
    process_data()
    generate_reports()
    send_emails()

    # Ping on success
    ping_heartbeat()

if __name__ == '__main__':
    try:
        run_job()
    except Exception as e:
        print(f"Job failed: {e}")
        raise  # Don't ping — missing ping = alert

If you don't want to add requests as a dependency, use the standard library:

import urllib.request

def ping_heartbeat():
    if not HEARTBEAT_URL:
        return
    try:
        urllib.request.urlopen(HEARTBEAT_URL, timeout=10)
    except Exception as e:
        print(f"Heartbeat ping failed: {e}")

For Django management commands or Celery tasks, add the ping at the end of the handle() method or task function after all work has completed.


Go

# main.go

package main

import (
    "fmt"
    "net/http"
    "os"
    "time"
)

func pingHeartbeat() {
    url := os.Getenv("PINGBASE_HEARTBEAT_URL")
    if url == "" {
        return
    }
    client := &http.Client{Timeout: 10 * time.Second}
    resp, err := client.Get(url)
    if err != nil {
        fmt.Fprintf(os.Stderr, "heartbeat ping failed: %v\n", err)
        return
    }
    resp.Body.Close()
}

func runJob() error {
    // Your job logic
    if err := processData(); err != nil {
        return fmt.Errorf("processData: %w", err)
    }
    if err := sendReports(); err != nil {
        return fmt.Errorf("sendReports: %w", err)
    }
    return nil
}

func main() {
    if err := runJob(); err != nil {
        fmt.Fprintf(os.Stderr, "job failed: %v\n", err)
        os.Exit(1)
        // Don't ping — missing ping = alert
    }
    pingHeartbeat()
}

The pattern is the same across languages: ping only on success, let failure be silent. The missing ping is the alert.


Choosing the right schedule and grace period

When creating a heartbeat monitor in PingBase, you set two values:

Job type Period Grace period
Every-minute health worker1 min1 min
Hourly data sync60 min10 min
Nightly backup24 hrs30 min
Weekly report generation7 days2 hrs
Queue processor (continuous)5 min5 min

Jobs worth monitoring with heartbeats

Any job that runs on a schedule and produces no user-visible output is a heartbeat monitoring candidate. Common examples:

If the job failed silently for a week before you found out, would you care? If yes, add a heartbeat monitor.

Monitor your cron jobs with PingBase

Heartbeat monitors for background tasks are included on all plans. Free for up to 5 monitors.

Get started free →

Related