Running Background Tasks
In this section, we will learn how to run background tasks in Django using Celery. Celery is a powerful task queue that lets you run slow, time-consuming jobs in the background, instead of making the user wait for them to finish. This is really useful for things like sending emails, processing files, or doing heavy calculations, without freezing up your main application.
Redis gets used for a few different jobs inside a Django app - it works as a message broker for Celery, and it’s also used for caching. It’s an in-memory data store, which means it keeps data in memory (RAM) instead of on disk, and it can be used as a database, a cache, and a message broker, all at once.
Installing Redis
Section titled “Installing Redis”We’ll use Docker to run Redis on our own computer. Add the following service to your docker-compose.yml file:
-
Add the Redis service to your
docker-compose.yml:services:redis:image: rediscontainer_name: redisrestart: alwaysports:- 6379:6379volumes:- ./data/redis:/datacommand: ["redis-server", "--appendonly", "yes"] # Enable data persistence (optional)redisinsight:image: redislabs/redisinsightcontainer_name: redisinsightports:- 8001:8001volumes:- ./data/redisinsight:/db -
Start the Redis server using Docker Compose:
Terminal window docker-compose up -d redis redisinsight -
To use Redis inside your Django app, install the
redisPython client package:Terminal window uv add redis
This will start both the Redis server and RedisInsight, which is a web-based tool you can use to look inside and manage Redis. You can open RedisInsight at http://localhost:8001 to keep an eye on your Redis instance.
Celery
Section titled “Celery”Celery is a powerful task queue that lets you run background tasks without making the main request wait around for them to finish. It helps you move slow, time-consuming jobs away from your main application thread, so your app feels faster and more responsive to users.
Installing Celery
Section titled “Installing Celery”To install Celery, follow the steps below based on which operating system you’re using:
Best option: Switch to Linux Ubuntu, or use GitHub Codespaces, to avoid all the manual setup headaches. If you’d still rather run Celery on Windows, you can use WSL (Windows Subsystem for Linux) instead. Here’s how to set it up:
- Install Celery in your Django project:
Terminal window uv add celery - Set up WSL (Windows Subsystem for Linux), and then just follow the normal Linux steps to run Celery inside the WSL environment.
-
Install WSL and a Linux distribution (like Ubuntu) from the Microsoft Store:
Terminal window wsl --install -
Install Python and the packages you need, inside the WSL environment:
Terminal window sudo apt updatesudo apt install python3 python3-pip -
Export your project’s dependencies into a
requirements.txtfile, so WSL can use them too:Terminal window uv export --format requirements-txt > requirements.txt# oruv export --no-hashes > requirements.txt# oruv export --no-dev > requirements.txt# oruv pip compile pyproject.toml -o requirements.txtUpdate your
.gitignorefile to include therequirements.txtfile and your virtual environment folder, so you don’t accidentally commit them to version control:# .gitignorerequirements.txtwsl_venv/ -
Go to your project folder in the WSL terminal, and create a virtual environment:
Terminal window python3 -m venv wsl_venvsource wsl_venv/bin/activatepip install -r requirements.txt -
Start the Celery worker inside the WSL terminal:
Terminal window celery -A your_project_name worker --loglevel=info -
In a second WSL terminal window, start the Celery beat scheduler:
Terminal window celery -A your_project_name beat --loglevel=info
-
- Install Celery in your Django project:
Terminal window uv add celery - Start the Celery worker, so it can start processing tasks:
Terminal window celery -A your_project_name worker --loglevel=info - In another terminal, start the Celery beat scheduler, which handles tasks that need to repeat on a schedule:
Terminal window celery -A your_project_name beat --loglevel=info
Configuring Celery
Section titled “Configuring Celery”To set up Celery in your Django project, follow these steps:
-
Create a
celery.pyfile inside your Django project folder (right next tosettings.py):# your_project_name/celery.pyimport osfrom celery import Celeryos.environ.setdefault('DJANGO_SETTINGS_MODULE', 'your_project_name.settings')celery = Celery('your_project_name')celery.config_from_object('django.conf:settings', namespace='CELERY')celery.autodiscover_tasks() -
Update your Django
__init__.pyfile, so it includes the Celery app:# your_project_name/__init__.pyfrom .celery import celery as celery_app__all__ = ('celery_app',) -
Set up Celery’s settings inside your
settings.pyfile:# settings.pyfrom celery.schedules import crontabCELERY_BROKER_URL = 'redis://localhost:6379/1'CELERY_RESULT_BACKEND = 'redis://localhost:6379/2'CELERY_BEAT_SCHEDULE = {'send-notifications-every-minute': {# format: 'your_project_name.app_name.tasks.task_name''task': 'your_project_name.tasks.notify_user',# crontab docs: https://docs.celeryq.dev/en/stable/userguide/periodic-tasks.html#crontab-schedules'schedule': crontab(minute='*'), # Run every minute'args': ["My Notification Message"],'kwargs': {}},}Here, we’re telling Celery to use Redis as both the message broker and the place where it stores task results. Looking at the URLs:
redis://localhost:6379/1uses Redis database 1 as the broker (this is where pending tasks get stored)redis://localhost:6379/2uses Redis database 2 to store the results of finished tasks
Using two separate databases like this stops data from different Celery parts from getting mixed up together.
-
Create a
tasks.pyfile in one of your Django apps, where you’ll define your background tasks:# your_project_name/tasks.pyfrom celery import shared_task@shared_taskdef notify_user(message):# Simulate a time-consuming taskimport timetime.sleep(5)print(f"Notification sent successfully: {message}")# your_project_name/tasks.pyfrom your_project_name.celery import celery@celery.taskdef notify_user(message):# Simulate a time-consuming taskimport timetime.sleep(5)print(f"Notification sent successfully: {message}") -
Start the Celery worker and the beat scheduler, each in its own terminal window:
Terminal window # Terminal 1: Start the worker process (processes queued tasks)celery -A your_project_name worker --loglevel=info# Terminal 2: Start the beat scheduler (triggers periodic tasks)celery -A your_project_name beat --loglevel=info
Monitoring Celery Tasks
Section titled “Monitoring Celery Tasks”If you want to keep an eye on your Celery tasks, you can use a package called flower. It gives you a web-based dashboard where you can watch your Celery workers and tasks in real time.
- Install Flower:
Terminal window uv add flower - Start the Flower server in a new terminal:
Terminal window celery -A your_project_name flower --port=5555 - Open the Flower dashboard at
http://localhost:5555to watch your Celery workers, tasks, and past task history.
Summary
Section titled “Summary”By following this guide, you have:
- Set up Redis as a message broker and a place to store task results
- Set up Celery inside your Django project
- Created background tasks using the
@shared_taskdecorator - Learned how to run the worker and beat processes
- Turned on monitoring with Flower, so you can see what’s happening with your tasks
Background tasks are a key part of building Django apps that can scale well and handle long, slow jobs without making users wait around.