Skip to content

Django: django-celery-beat periodic tasks

Django has no scheduler of its own: its periodic tasks run through Celery beat, and with django-celery-beat they are kept in the database, editable in the admin. The Python library reads them with the Celery integration, with nothing to configure.

Scheduler Install What it reads
django-celery-beat pip install "silencewatch[django-celery-beat]" the enabled periodic tasks in the database, crontab and interval

Python 3.9 or later, celery 5.2 or later, django-celery-beat 2.5 or later.

Create the API key in your project’s Settings › API keys and give it to the process:

Terminal window
export SILENCEWATCH_API_KEY=sw_...
# export SILENCEWATCH_URL=https://watch.example.com # only when you host SilenceWatch yourself

Then instrument the Celery app, as you would without Django:

proj/celery.py
from celery import Celery
from silencewatch.integrations.celery import instrument
app = Celery("proj")
app.config_from_object("django.conf:settings", namespace="CELERY")
app.autodiscover_tasks()
instrument(app)
settings.py
INSTALLED_APPS = [
# …
"django_celery_beat",
]

When django_celery_beat is an installed Django app, the same instrument(app) also reads the enabled periodic tasks of the database, crontab (with its own time zone) and interval, next to the beat_schedule. Everything else (the declaration, the heartbeats, the identity of a task, the settings) is that of the Celery integration.

A change made while the workers run, in the Django admin or in code, is declared again within a minute. Each worker process looks at django-celery-beat’s own change marker, one row, every minute, and reads and declares the schedule only when it moved. The new task’s runs are reported from then on, without a restart.

It is done per worker process, on purpose: a pool child forked before the change only knows the ping keys it was forked with. The thread closes the database connections it opens, so it cannot hold one for the pool.

  • One-off and clocked tasks run once and are not a schedule to watch: they are not declared.
  • Disabled tasks are left out. If they had been declared, they are flagged orphaned, and declared again when they are enabled.
  • A task in both the beat_schedule and the database is one check, declared from the schedule that fires most often.
  • Solar schedules are not read.
  • A task that the Celery integration cannot express is skipped with a warning naming it, rather than failing the whole declaration.