Skip to content

Running the Scheduler

django-periodic-tasks runs as a daemon thread inside your Django process. Enable it with a single setting and the scheduler starts automatically alongside your application.

Add the following to your Django settings:

# settings.py
PERIODIC_TASKS_AUTOSTART = True

When Django starts, AppConfig.ready() launches the scheduler as a daemon thread. It syncs code-defined schedules to the database, then enters a tick-sleep loop that checks for due tasks every PERIODIC_TASKS_SCHEDULER_INTERVAL seconds (default: 15).

The daemon thread exits automatically when the main process shuts down — no signal handling or cleanup is needed on your part.

Tuning the Interval

PERIODIC_TASKS_SCHEDULER_INTERVAL = 30  # check every 30 seconds

Lower values mean tasks are enqueued closer to their scheduled time but increase database load. The default of 15 seconds is a good balance for most applications.

Standalone Scheduler

For cases where you want the scheduler in its own dedicated process (e.g. separating scheduling from request serving), use the run_scheduler management command:

python manage.py run_scheduler

This runs the scheduler loop in the main thread (blocking). You’ll need to run your task worker separately.

Options

Flag Default Description
--interval 15 Seconds between scheduler ticks
-v 0 Suppress all output
-v 2 Enable debug logging

Multi-Worker Deployment

The scheduler is safe to run across multiple processes. Each scheduler tick uses SELECT FOR UPDATE SKIP LOCKED to claim due tasks, so even if multiple scheduler instances run simultaneously, each task is enqueued exactly once.

A typical production setup with two web/worker processes:

┌─────────────────────────┐  ┌─────────────────────────┐
│   gunicorn / daphne     │  │   gunicorn / daphne     │
│   (autostart=True)      │  │   (autostart=True)      │
│   scheduler + app       │  │   scheduler + app       │
└─────────────────────────┘  └─────────────────────────┘
         │                            │
         └──────────┬─────────────────┘
                    │
           ┌────────▼────────┐
           │   PostgreSQL    │
           │  (shared DB)    │
           └─────────────────┘

Both instances run the scheduler, but SKIP LOCKED ensures no duplicate enqueues. Scale horizontally by adding more processes.

Graceful Shutdown

The run_scheduler command handles SIGINT and SIGTERM for graceful shutdown:

  • The current scheduler tick completes.
  • The stop event is set, and the scheduler thread exits.
  • The process terminates cleanly.

When using autostart, the scheduler thread is a daemon thread and exits automatically when the main process terminates. In container environments (Docker, Kubernetes), this means docker stop and Kubernetes pod termination work correctly out of the box.