intermediate 14 min read

How to Set Up Cron Jobs in Django & Python

Learn how to schedule cron jobs in Django using Celery Beat, django-crontab, APScheduler, and management commands. Covers setup, testing, and production deployment.

Prerequisites

  • Python 3.10+
  • Django 4.2+
  • pip
  • Redis (for Celery Beat)

Cron Options in the Python Ecosystem

Python and Django offer several approaches to scheduled tasks:

  • System crontab + management commands — Simplest approach, no extra packages
  • django-crontab — Manages crontab entries from Django settings
  • Celery Beat — Production-grade distributed task scheduler
  • APScheduler — In-process scheduler, no external dependencies

For most production apps, Celery Beat is the standard choice. For simple projects, a management command with system cron works well.

Quick Start: Management Command + System Cron

Step 1: Create a management command

python
# myapp/management/commands/send_daily_report.py
from django.core.management.base import BaseCommand
from myapp.services import generate_report

class Command(BaseCommand):
    help = 'Sends the daily report email'

    def handle(self, *args, **options):
        count = generate_report()
        self.stdout.write(self.style.SUCCESS(f'Sent {count} reports'))

Step 2: Add to crontab

bash
crontab -e
# Run daily at 8 AM
0 8 * * * cd /opt/myapp && /opt/myapp/venv/bin/python manage.py send_daily_report >> /var/log/cron.log 2>&1

Always use the full path to the Python binary inside your virtualenv.

Celery Beat for Production

Celery Beat is the standard for production Django apps.

Install:

bash
pip install celery redis django-celery-beat

Configure Celery (myproject/celery.py):

python
import os
from celery import Celery

os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'myproject.settings')

app = Celery('myproject')
app.config_from_object('django.conf:settings', namespace='CELERY')
app.autodiscover_tasks()

Define a periodic task (myapp/tasks.py):

python
from celery import shared_task

@shared_task
def cleanup_expired_sessions():
    from django.core.management import call_command
    call_command('clearsessions')

Schedule it (settings.py):

python
from celery.schedules import crontab

CELERY_BROKER_URL = 'redis://localhost:6379/0'

CELERY_BEAT_SCHEDULE = {
    'cleanup-sessions': {
        'task': 'myapp.tasks.cleanup_expired_sessions',
        'schedule': crontab(hour=2, minute=0),
    },
    'send-reminders': {
        'task': 'myapp.tasks.send_reminders',
        'schedule': crontab(minute='*/15'),
    },
}

Run:

bash
celery -A myproject beat -l info
celery -A myproject worker -l info

django-crontab: Simple Cron Management

Manages system crontab entries from Django settings:

bash
pip install django-crontab
python
# settings.py
INSTALLED_APPS = ['django_crontab', ...]

CRONJOBS = [
    ('*/5 * * * *', 'myapp.cron.my_scheduled_job'),
    ('0 0 * * *', 'django.core.management.call_command', ['clearsessions']),
]
bash
python manage.py crontab add     # Install cron jobs
python manage.py crontab show    # List installed jobs
python manage.py crontab remove  # Remove all jobs

Common Cron Expressions

Standard 5-field format used across all Python cron tools:

  • */5 * * * * — Every 5 minutes
  • 0 * * * * — Every hour
  • 0 0 * * * — Daily at midnight
  • 0 9 * * 1-5 — Weekdays at 9 AM
  • 0 0 1 * * — First day of every month

In Celery Beat, use the crontab() helper:

python
from celery.schedules import crontab

crontab(minute='*/5')
crontab(hour=0, minute=0)
crontab(hour=9, minute=0, day_of_week='1-5')

Every weekday at 9:00 AM

Next runs (UTC):

Mon, May 18, 2026 09:00

Tue, May 19, 2026 09:00

Wed, May 20, 2026 09:00

Testing Scheduled Tasks

Test management commands:

python
from django.test import TestCase
from django.core.management import call_command
from io import StringIO

class CronJobTest(TestCase):
    def test_daily_report(self):
        out = StringIO()
        call_command('send_daily_report', stdout=out)
        self.assertIn('Sent', out.getvalue())

Test Celery tasks synchronously:

python
# settings/test.py
CELERY_TASK_ALWAYS_EAGER = True
python
from myapp.tasks import cleanup_expired_sessions

def test_cleanup():
    result = cleanup_expired_sessions()

Debugging Cron Issues

Cron job doesn't run:

  • Check the virtualenv path: use the full path to python
  • Set DJANGO_SETTINGS_MODULE explicitly in the crontab entry
  • Check cron logs: grep CRON /var/log/syslog

Celery Beat not dispatching:

  • Ensure Redis is running: redis-cli ping
  • Check the beat process: celery -A myproject beat -l debug
  • Verify schedules: celery -A myproject inspect scheduled

Production Tips

Use Django's logging framework:

python
import logging
logger = logging.getLogger(__name__)

def my_job():
    logger.info('Job started')
    try:
        do_work()
    except Exception:
        logger.exception('Job failed')

Supervisor config for Celery:

ini
[program:celery-worker]
command=/opt/venv/bin/celery -A myproject worker -l info
directory=/opt/app
user=www-data
autostart=true
autorestart=true

[program:celery-beat]
command=/opt/venv/bin/celery -A myproject beat -l info
directory=/opt/app
user=www-data
autostart=true
autorestart=true

Frequently Asked Questions