Celery
Task Management
Asynchronous Programming
Python
Priority Queue

How to use priority in celery task.apply_async

Master System Design with Codemia

Enhance your system design skills with over 120 practice problems, detailed solutions, and hands-on exercises.

Introduction

Celery lets you attach a priority to tasks sent with apply_async, but the setting only matters if the broker and queue are configured to honor it. The practical answer is to declare priority-capable queues, send tasks with the priority argument, and remember that priority influences scheduling order rather than creating a hard real-time guarantee.

Send a Task with priority

The call site is straightforward. Pass priority to apply_async when you enqueue the task.

python
1from celery import Celery
2
3app = Celery("demo", broker="pyamqp://guest@localhost//")
4
5@app.task
6def compute(values):
7    return sum(values)
8
9compute.apply_async(args=[[1, 2, 3]], priority=9)
10compute.apply_async(args=[[4, 5, 6]], priority=1)

The actual meaning of higher and lower numbers depends on the broker. With RabbitMQ, higher numeric values are typically treated as higher priority within a queue configured for priorities.

Configure the Queue to Support Priorities

Passing priority alone is not enough. The queue must declare a maximum priority.

python
1from kombu import Exchange, Queue
2
3app.conf.task_queues = (
4    Queue(
5        "default",
6        Exchange("default"),
7        routing_key="default",
8        queue_arguments={"x-max-priority": 10},
9    ),
10)
11
12app.conf.task_default_queue = "default"
13app.conf.task_default_routing_key = "default"

Without the queue argument, many brokers will accept the task but ignore the priority metadata.

Route Urgent Tasks Intentionally

Priority works best when combined with explicit routing. If all tasks share one busy queue, high-priority items can still be delayed by tasks already prefetched by workers.

python
1app.conf.task_routes = {
2    "demo.compute": {"queue": "default"},
3    "demo.send_alert": {"queue": "critical"},
4}
5
6app.conf.task_queues += (
7    Queue(
8        "critical",
9        Exchange("critical"),
10        routing_key="critical",
11        queue_arguments={"x-max-priority": 10},
12    ),
13)

In practice, separate queues for materially different workloads are often clearer than relying on one queue with many priority levels.

Understand Worker Prefetch Behavior

A common surprise is that a supposedly urgent task waits behind lower-priority tasks that have already been prefetched by workers. Celery workers reserve tasks before executing them, which can reduce the visible effect of priority.

python
app.conf.worker_prefetch_multiplier = 1

A lower prefetch value can make priority behavior more responsive, especially when task duration varies widely. The tradeoff is lower throughput in some workloads.

Use Priority as a Scheduling Hint, Not a Contract

Priority improves the odds that urgent tasks run sooner, but it does not bypass every queueing or worker-level constraint. Long-running tasks, insufficient worker capacity, or poor routing can still overwhelm the system.

If an operation is truly critical, consider a dedicated queue and dedicated workers instead of relying only on numeric priority.

Verify Behavior with Small Tests

Test with visible task durations so you can confirm what the broker and workers are actually doing.

python
1import time
2
3@app.task
4def slow_task(name, seconds):
5    print(f"starting {name}")
6    time.sleep(seconds)
7    print(f"finished {name}")
8
9slow_task.apply_async(args=["low", 5], priority=1)
10slow_task.apply_async(args=["high", 1], priority=9)

Observe worker logs before assuming the configuration works. Many priority problems are really queue declaration or prefetch problems.

Common Pitfalls

  • Passing priority while using a broker or queue configuration that does not support priorities.
  • Assuming lower and higher numbers mean the same thing across all broker setups without checking documentation.
  • Expecting priority to override tasks that workers have already prefetched.
  • Putting every workload into one queue instead of separating genuinely critical tasks.
  • Treating task priority as a hard guarantee instead of a scheduling hint.

Summary

  • Use apply_async(..., priority=...) to attach a priority to a Celery task.
  • Configure queues with x-max-priority so the broker can honor it.
  • Combine priority with routing and queue design, not as a standalone fix.
  • Tune worker prefetch if urgent tasks still wait too long.
  • Test the behavior with real workers instead of assuming the broker uses priority the way you expect.

Course illustration
Course illustration

All Rights Reserved.