Help & guidesHeartbeat (cron monitoring) › Add to cron job (bash, Python, Node)

Add to cron job (bash, Python, Node)

4 min read · Heartbeat (cron monitoring)

Goal: Add the heartbeat URL into an existing cron job or script so that ePulz.io receives an "I am alive" ping after every successful run.

Principle

You call the heartbeat URL after a successful completion of the task. If the script fails, the URL is not called, ePulz.io notices the missing ping and sends you an alert.

Bash / shell cron

Add a curl call to the end of your cron line joined with the && operator:

# /etc/crontab or crontab -e
0 3 * * * /usr/local/bin/backup.sh && curl -fsS -m 10 \
  https://epulz.io/heartbeat/Qs78OPNIIsCF_-Vj > /dev/null

curl flags:

  • -f = fail on non-2xx response (curl returns an error)
  • -s = silent (does not print progress)
  • -S = but shows errors (combined with -s)
  • -m 10 = max-time 10 seconds (timeout)
  • > /dev/null = response does not flood the cron log

Python

For Python scripts add a call via requests or urllib:

import os
import requests

HEARTBEAT_URL = os.environ.get("HEARTBEAT_URL")

def sync_data():
    # ... your logic ...
    pass

try:
    sync_data()
    # Heartbeat is sent only if sync_data() does not raise an exception
    if HEARTBEAT_URL:
        requests.get(HEARTBEAT_URL, timeout=10)
except Exception as e:
    # Heartbeat is not sent - ePulz.io will alert you
    print(f"Sync failed: {e}")
    raise

Without an external library, only with the standard library:

import urllib.request

# After successful task completion
urllib.request.urlopen("https://epulz.io/heartbeat/Qs78OPNIIsCF_-Vj", timeout=10)

Node.js

const HEARTBEAT_URL = process.env.HEARTBEAT_URL;

async function nightlyJob() {
  await processInvoices();
  // Heartbeat only after successful completion
  await fetch(HEARTBEAT_URL, { signal: AbortSignal.timeout(10000) });
}

nightlyJob().catch(err => {
  console.error(err);
  process.exit(1);  // Heartbeat will not be sent, ePulz.io will alert you
});

Docker / systemd timer

For a systemd timer, add ExecStartPost to the .service file:

# /etc/systemd/system/db-backup.service
[Service]
Type=oneshot
ExecStart=/usr/local/bin/backup.sh
ExecStartPost=/usr/bin/curl -fsS -m 10 https://epulz.io/heartbeat/Qs78OPNIIsCF_-Vj

ExecStartPost runs only if ExecStart was successful.

PHP / Laravel scheduler

// app/Console/Kernel.php
$schedule->command('backup:run')
    ->daily()
    ->thenPing('https://epulz.io/heartbeat/Qs78OPNIIsCF_-Vj');

Laravel has a built-in thenPing() method exactly for this purpose.

Advanced: ping at the beginning and at the end

For more thorough detection you can also send a "start" ping (the task has started) - this lets you distinguish "the task did not start at all" from "the task got stuck":

# Start ping
curl -fsS -m 10 https://epulz.io/heartbeat/Qs78OPNIIsCF_-Vj/start > /dev/null

# Your task
/usr/local/bin/backup.sh
EXIT_CODE=$?

# Done ping with exit code
curl -fsS -m 10 "https://epulz.io/heartbeat/Qs78OPNIIsCF_-Vj?exit=$EXIT_CODE" > /dev/null

Test - simulation of a single run

Before adding to cron, test the URL manually:

$ curl -fsS https://epulz.io/heartbeat/Qs78OPNIIsCF_-Vj
OK

After a successful call, in the ePulz.io dashboard you will see the monitor state move from Pending to OK.