1 Core Concepts
Tasks
Units of work decorated with @app.task or @shared_task. Tasks are serialized (JSON/pickle), pushed to a message broker, and executed asynchronously by workers. Perfect for LLM API calls, file processing, and email sending.
Broker
The message transport layer. Redis (lightweight, fast) and RabbitMQ (durable, feature-rich) are the primary choices. The broker holds task messages in queues until workers consume them. Redis is the most popular for agent backends.
Workers
Processes that consume tasks from the broker and execute them. Each worker runs a prefork pool (multiple child processes) by default. Workers can be scaled horizontally — add more workers to increase throughput linearly.
Result Backend
Optional store for task return values and status. Use Redis, PostgreSQL, or Django ORM as the backend. Callers can check result.status and result.get() to retrieve the outcome of an async task.
2 Setup Guide
📦 Installation & Configuration
🚀 Starting Workers
3 Key Patterns & Usage
Defining & Calling Tasks
Chains, Groups & Chords
Retry & Error Handling
Periodic Tasks (Celery Beat)
4 Queues & Task Routing
Named Queues
Route tasks to dedicated queues: high_priority, agent_inference, bulk_processing. Run separate worker processes per queue. Prevents slow bulk jobs from blocking latency-sensitive agent responses.
Task Routing
Use CELERY_TASK_ROUTES to auto-route tasks by name pattern: 'agents.*': {'queue': 'agents'}. Or route explicitly: task.apply_async(queue='high_priority'). Keeps queues focused and workers specialized.
Priority & Rate Limiting
Set per-task rate limits: @shared_task(rate_limit='10/m') caps at 10 executions/minute per worker. Use priority queues (RabbitMQ) for urgent agent tasks. Combine with countdown for delayed execution.
5 Monitoring & Observability
Flower Dashboard
Real-time web monitor at :5555. Shows active workers, task throughput, success/failure rates, and queue depths. Supports basic auth. Essential for debugging stuck or failing agent tasks in production.
Prometheus Metrics
Export Celery metrics with celery-exporter. Track task duration, queue lengths, worker pool utilization, and retry counts in Grafana. Set alerts on queue depth > 1000 or failure rate > 5%.
Dead Letter Queues
Tasks that exhaust all retries should be routed to a dead letter queue (DLQ) instead of being silently dropped. Inspect DLQ periodically to identify systematic failures (API outages, data corruption). Replay tasks after fixing the root cause.
6 Scaling & Production
Horizontal Worker Scaling
Add more worker processes across machines — they all consume from the same broker. Use Kubernetes Deployments or autoscaling groups to scale workers based on queue depth. Each worker runs 4-16 concurrent child processes (prefork pool).
Prefork vs. Gevent Pool
Default prefork pool uses OS processes — best for CPU-bound tasks. For I/O-bound tasks (API calls, DB queries), use --pool=gevent --concurrency=100 to run 100 green threads per worker. 10× throughput for agent inference workloads.
Broker Scaling (Redis)
A single Redis instance handles ~100K tasks/sec. For higher throughput, use Redis Sentinel (HA) or Redis Cluster. Alternatively, use RabbitMQ with mirrored queues for durable message delivery and built-in dead-letter exchanges.
Task Idempotency
Tasks may execute more than once (broker redelivery, worker crash). Design tasks to be idempotent — running twice produces the same result. Use unique task IDs as dedup keys in Redis. Check if not already_processed(task_id) before executing.
Memory Leak Prevention
Set worker_max_tasks_per_child=1000 to restart child processes after 1000 tasks. Prevents memory leaks from accumulating in long-running workers. Also set worker_max_memory_per_child=400000 (400 MB) for a hard memory limit.
Graceful Shutdown
Send SIGTERM for warm shutdown — workers finish current tasks before exiting. Set worker_cancel_long_running_tasks_on_connection_loss=True for Kubernetes pod termination. Use acks_late=True to requeue unfinished tasks on crash.
7 FAQ
Redis vs. RabbitMQ — which broker should I use?→
How do I handle tasks that take 30+ minutes?→
time_limit=3600 (1 hour) and soft_time_limit=1800 (30 min). Use acks_late=True so the task is requeued if the worker crashes mid-execution. Emit progress updates via self.update_state(state='PROGRESS', meta={'percent': 50}) so the caller can poll status.Can I use Celery without Django?→
Celery() app directly, configure the broker URL, and define tasks. Works with Flask, FastAPI, or standalone Python scripts. Django integration (django-celery-results, django-celery-beat) is optional — it adds admin UI and ORM-backed result storage.How do I ensure exactly-once task execution?→
task_id as a deduplication key in Redis or your database. Check if the task was already processed before executing the main logic. Combine with acks_late=True for crash safety.How do I run Celery in Kubernetes?→
terminationGracePeriodSeconds: 120 for warm shutdown. Scale replicas based on queue depth using KEDA (Kubernetes Event-Driven Autoscaler). Run Celery Beat as a single-replica Deployment to avoid duplicate scheduled tasks. Use acks_late=True so tasks survive pod restarts.