Skip to content

Monitor Celery beat tasks

Celery beat schedules tasks; workers run them. Either half can stop on its own: beat isn’t running after a deploy, workers are down or stuck, or the task starts raising. A heartbeat check catches all of these: the task reports to Pingstack after it succeeds, and Pingstack alerts you when that report doesn’t arrive on time.

1. Create a heartbeat check that matches the schedule

Section titled “1. Create a heartbeat check that matches the schedule”

Find the task in your beat schedule:

from celery.schedules import crontab
app.conf.beat_schedule = {
"nightly-report": {
"task": "reports.tasks.send_nightly_report",
"schedule": crontab(hour=2, minute=0),
},
}

Create a heartbeat check and set its schedule to Cron with the matching expression (0 2 * * * here).

Beat runs schedules in Celery’s configured timezone: app.conf.timezone, or CELERY_TIMEZONE in Django settings. It defaults to UTC. Choose the same timezone in Pingstack. See schedules and timezones.

Set a grace period that covers the task’s runtime, time waiting in the queue and any retries. Attach a notification group.

Copy the location’s ping URL from the check in Pingstack and set it as an environment variable for your workers, such as PINGSTACK_NIGHTLY_REPORT_URL, through your deployment’s secret store. Don’t commit it to settings files.

Add a small helper and call it as the task’s last line:

reports/heartbeat.py
import logging
import os
import requests
logger = logging.getLogger(__name__)
def report(env_var, value=None):
url = os.environ.get(env_var)
if not url:
return
params = {"value": value} if value is not None else None
try:
requests.get(url, params=params, timeout=10).raise_for_status()
except requests.RequestException as error:
logger.warning("Pingstack heartbeat failed: %s", type(error).__name__)
reports/tasks.py
from celery import shared_task
from .heartbeat import report
@shared_task
def send_nightly_report():
build_and_send_report()
report("PINGSTACK_NIGHTLY_REPORT_URL")

The report runs only if the work before it finished without raising, so a failed run sends nothing and Pingstack alerts you when the deadline passes. A failure to reach Pingstack is logged, not raised, so it can’t make the task retry.

Run the task once through a worker:

Terminal window
celery -A proj call reports.tasks.send_nightly_report

Open the check in Pingstack and confirm the location received a heartbeat and shows a next deadline.

Then confirm beat runs in production, as exactly one process:

Terminal window
celery -A proj beat --loglevel=info

Running two beat processes schedules every task twice. Running none schedules nothing, and every periodic check alerts.

Pass a count the task produces as a numeric value:

sent = build_and_send_report()
report("PINGSTACK_NIGHTLY_REPORT_URL", value=sent)

Set warning and critical thresholds on the check, so a run that succeeds but sends nothing still raises an alert.

  • Queue backlog. Beat sends the task on time but it waits behind other work. Lengthen the grace period, or route it to its own queue.
  • Retries. A task that succeeds on a later retry reports late.
  • Timezone mismatch. The check and Celery’s timezone must agree.
  • Task expiry. A task with expires set may be discarded if workers pick it up too late. It sends nothing, and Pingstack alerts.

If alerts still don’t match what you expect, see troubleshooting monitoring.