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)
In this guide
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
# 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
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>&1Always 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:
pip install celery redis django-celery-beatConfigure Celery (myproject/celery.py):
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):
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):
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:
celery -A myproject beat -l info
celery -A myproject worker -l infodjango-crontab: Simple Cron Management
Manages system crontab entries from Django settings:
pip install django-crontab# settings.py
INSTALLED_APPS = ['django_crontab', ...]
CRONJOBS = [
('*/5 * * * *', 'myapp.cron.my_scheduled_job'),
('0 0 * * *', 'django.core.management.call_command', ['clearsessions']),
]python manage.py crontab add # Install cron jobs
python manage.py crontab show # List installed jobs
python manage.py crontab remove # Remove all jobsCommon Cron Expressions
Standard 5-field format used across all Python cron tools:
*/5 * * * *— Every 5 minutes0 * * * *— Every hour0 0 * * *— Daily at midnight0 9 * * 1-5— Weekdays at 9 AM0 0 1 * *— First day of every month
In Celery Beat, use the crontab() helper:
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:
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:
# settings/test.py
CELERY_TASK_ALWAYS_EAGER = Truefrom 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_MODULEexplicitly 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:
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:
[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