Skip to main content

Command Palette

Search for a command to run...

Implementing Task Queues in Python Using Celery and Redis — Scalable Background Jobs

Updated
5 min read
Implementing Task Queues in Python Using Celery and Redis — Scalable Background Jobs
N

I am a Tech Enthusiast having 13+ years of experience in 𝐈𝐓 as a 𝐂𝐨𝐧𝐬𝐮𝐥𝐭𝐚𝐧𝐭, 𝐂𝐨𝐫𝐩𝐨𝐫𝐚𝐭𝐞 𝐓𝐫𝐚𝐢𝐧𝐞𝐫, 𝐌𝐞𝐧𝐭𝐨𝐫, with 12+ years in training and mentoring in 𝐒𝐨𝐟𝐭𝐰𝐚𝐫𝐞 𝐄𝐧𝐠𝐢𝐧𝐞𝐞𝐫𝐢𝐧𝐠, 𝐃𝐚𝐭𝐚 𝐄𝐧𝐠𝐢𝐧𝐞𝐞𝐫𝐢𝐧𝐠, 𝐓𝐞𝐬𝐭 𝐀𝐮𝐭𝐨𝐦𝐚𝐭𝐢𝐨𝐧 𝐚𝐧𝐝 𝐃𝐚𝐭𝐚 𝐒𝐜𝐢𝐞𝐧𝐜𝐞. I have 𝒕𝒓𝒂𝒊𝒏𝒆𝒅 𝒎𝒐𝒓𝒆 𝒕𝒉𝒂𝒏 10,000+ 𝑰𝑻 𝑷𝒓𝒐𝒇𝒆𝒔𝒔𝒊𝒐𝒏𝒂𝒍𝒔 and 𝒄𝒐𝒏𝒅𝒖𝒄𝒕𝒆𝒅 𝒎𝒐𝒓𝒆 𝒕𝒉𝒂𝒏 500+ 𝒕𝒓𝒂𝒊𝒏𝒊𝒏𝒈 𝒔𝒆𝒔𝒔𝒊𝒐𝒏𝒔 in the areas of 𝐒𝐨𝐟𝐭𝐰𝐚𝐫𝐞 𝐃𝐞𝐯𝐞𝐥𝐨𝐩𝐦𝐞𝐧𝐭, 𝐃𝐚𝐭𝐚 𝐄𝐧𝐠𝐢𝐧𝐞𝐞𝐫𝐢𝐧𝐠, 𝐂𝐥𝐨𝐮𝐝, 𝐃𝐚𝐭𝐚 𝐀𝐧𝐚𝐥𝐲𝐬𝐢𝐬, 𝐃𝐚𝐭𝐚 𝐕𝐢𝐬𝐮𝐚𝐥𝐢𝐳𝐚𝐭𝐢𝐨𝐧𝐬, 𝐀𝐫𝐭𝐢𝐟𝐢𝐜𝐢𝐚𝐥 𝐈𝐧𝐭𝐞𝐥𝐥𝐢𝐠𝐞𝐧𝐜𝐞 𝐚𝐧𝐝 𝐌𝐚𝐜𝐡𝐢𝐧𝐞 𝐋𝐞𝐚𝐫𝐧𝐢𝐧𝐠. I am interested in 𝐰𝐫𝐢𝐭𝐢𝐧𝐠 𝐛𝐥𝐨𝐠𝐬, 𝐬𝐡𝐚𝐫𝐢𝐧𝐠 𝐭𝐞𝐜𝐡𝐧𝐢𝐜𝐚𝐥 𝐤𝐧𝐨𝐰𝐥𝐞𝐝𝐠𝐞, 𝐬𝐨𝐥𝐯𝐢𝐧𝐠 𝐭𝐞𝐜𝐡𝐧𝐢𝐜𝐚𝐥 𝐢𝐬𝐬𝐮𝐞𝐬, 𝐫𝐞𝐚𝐝𝐢𝐧𝐠 𝐚𝐧𝐝 𝐥𝐞𝐚𝐫𝐧𝐢𝐧𝐠 new subjects.

Introduction

In the world of modern applications — APIs, web services, machine learning pipelines, IoT, and enterprise systems — it’s common to encounter tasks that shouldn’t or can’t be processed immediately within a request-response cycle. These include sending emails, resizing images, generating reports, making API calls, or training models.

Trying to perform these actions synchronously results in slow APIs, degraded UX, and inefficient resource usage. The ideal solution is to process these time-consuming operations asynchronously in the background using a task queue system.

Celery is one of Python’s most robust, mature, and production-grade libraries for managing asynchronous tasks and distributed job queues. Combined with a message broker like Redis, it can handle millions of background tasks efficiently.

This comprehensive guide covers:

  • Why you need task queues

  • How Celery works under the hood

  • Setting up Redis as a broker

  • Writing and queuing background jobs

  • Running Celery workers and monitoring queues

  • Handling task retries, results, and periodic scheduling

  • Deployment strategies for production-ready setups

Why You Need Task Queues

Modern applications need to perform certain operations asynchronously, outside the primary execution thread. Task queues enable:

  • Decoupling of long-running jobs from user-facing services

  • Retrying failed tasks automatically

  • Distributing load across multiple workers and machines

  • Scaling background job processing without affecting the core application

Common use cases:

  • Sending emails and SMS

  • Resizing images and videos

  • Processing uploads and downloads

  • Running scheduled jobs (CRON-like tasks)

  • Interacting with third-party services (payment gateways, APIs)

  • Training ML models and data ETL pipelines

What is Celery?

Celery is an open-source, distributed task queue library written in Python. It uses a message broker (like Redis, RabbitMQ) to send and receive task messages. Celery workers listen to these queues, pick up tasks, and execute them asynchronously.

Key features:

  • Supports multiple brokers: Redis, RabbitMQ, Amazon SQS

  • Highly scalable — can run on a single node or across distributed servers

  • Provides task retrying, time limits, rate limits, and priority queues

  • Built-in result backend for tracking task status and results

  • Native periodic task scheduling (beat scheduler)

  • Integrates well with Django, Flask, FastAPI, and standalone Python scripts

How Celery Works

Celery’s architecture involves four main components:

  1. Producer (Application/API): Places tasks into a queue.

  2. Broker (Redis): Acts as a message queue to deliver tasks to workers.

  3. Worker: Executes tasks asynchronously from the queue.

  4. Result Backend (optional): Stores task execution results for later retrieval.

When a task is called asynchronously, Celery serializes the task information (function name, arguments) and sends it to the broker. A worker process picks up the message, executes the function, and optionally stores the result.

Setting Up Redis as a Broker

First, install Redis locally or use a cloud-managed service.

On Ubuntu:

sudo apt-get install redis-server
sudo systemctl enable redis
sudo systemctl start redis

Test Redis:

redis-cli ping

It should return:

PONG

Installing Celery

Install Celery and the Redis client:

pip install celery redis

Creating a Basic Celery App

Create a new Python file called tasks.py.

from celery import Celery

app = Celery(
    'background_tasks',
    broker='redis://localhost:6379/0',
    backend='redis://localhost:6379/1'
)

@app.task
def add(x, y):
    return x + y

Explanation:

  • broker tells Celery where to send and fetch task messages.

  • backend stores the task result and status.

  • The @app.task decorator registers a function as a Celery task.

Running Celery Workers

Open a new terminal window and start a Celery worker:

celery -A tasks worker --loglevel=info

Options:

  • -A tasks refers to the file (without .py) where the Celery app is defined.

  • --loglevel=info outputs real-time logs.

You’ll see:

[tasks]
  . tasks.add

The worker is now ready to process tasks.

Sending Tasks Asynchronously

Now, in a Python shell or separate script:

from tasks import add

result = add.delay(5, 7)
print(result.id)

add.delay(5, 7) queues the task asynchronously.

Check the task status:

result.status

Retrieve the result:

result.get(timeout=10)

Retrying Failed Tasks

Celery can automatically retry failed tasks.

@app.task(bind=True, max_retries=3)
def risky_operation(self, x):
    try:
        if x == 0:
            raise ValueError("Invalid value")
        return 10 / x
    except Exception as exc:
        raise self.retry(exc=exc, countdown=5)

Parameters:

  • bind=True gives the task access to itself.

  • max_retries limits retry attempts.

  • countdown adds a delay before retrying.

Task Time Limits and Rate Limits

To prevent runaway tasks:

@app.task(time_limit=30, rate_limit='10/m')
def heavy_task():
    # Expensive operation
    pass

Explanation:

  • time_limit=30 enforces a 30-second max runtime.

  • rate_limit='10/m' limits to 10 executions per minute.

Result Backend and Storing Task Results

The result backend can store the outcome and status of tasks. Redis works well for this.

Retrieving results:

result = add.delay(4, 6)
print(result.get(timeout=5))

You can query task status:

print(result.ready())   # True or False
print(result.successful())

Alternative backends: RabbitMQ, MySQL, PostgreSQL, Amazon S3, Memcached.

Periodic Task Scheduling

Use Celery Beat, a scheduler service that kicks off tasks at regular intervals.

Install:

pip install django-celery-beat

Create a scheduler config:

from celery.schedules import crontab

app.conf.beat_schedule = {
    'send-reminder-every-minute': {
        'task': 'tasks.send_reminder',
        'schedule': crontab(minute='*'),
    },
}

Run the scheduler:

celery -A tasks beat --loglevel=info

Now send_reminder runs every minute.

Chaining and Grouping Tasks

Celery supports complex workflows.

Task Chaining

from celery import chain

chain(add.s(4, 6) | add.s(10))()

Result: (4+6)=10, then 10+10=20

Parallel Execution (Groups)

from celery import group

group(add.s(2, 2), add.s(3, 3))().get()

Deployment Strategies

In production:

  • Use supervisord, systemd, or Docker to manage Celery worker processes.

  • Run multiple worker instances across machines.

  • Use Redis Sentinel or Amazon ElastiCache for highly available Redis brokers.

  • Monitor queues with tools like Flower (pip install flower).

Launch Flower:

celery -A tasks flower

Access it at http://localhost:5555

Performance Tips

  • Always set a time_limit on long-running tasks.

  • Avoid storing large payloads in Redis result backend.

  • Use task routing and named queues for load balancing.

  • Disable result storage for fire-and-forget tasks.

  • Compress messages with app.conf.task_compression = 'gzip'

Conclusion

Asynchronous background processing is indispensable in modern application development. Python’s Celery, paired with Redis, offers a scalable, reliable, and feature-rich solution for handling distributed task queues.

This guide covered:

  • Celery’s architecture

  • Task creation and asynchronous execution

  • Worker management and scheduling

  • Task retries, chaining, and result storage

  • Production deployment and performance optimization

More from this blog

Naveen P.N's Tech Blog

95 posts