Distributed task queue · async jobs, scheduling, workflows · verified against Celery 5.6 (2026)

Celery cheat sheet

Celery runs Python work outside the request — async tasks, scheduled jobs, and multi-step workflows — across a pool of workers that pull from a broker (Redis/RabbitMQ). You define tasks with @app.task, call them with .delay(), and a worker executes them; results (optional) go to a result backend. This sheet targets Celery 5.6 (Python 3.9+). Common in ML/data for training jobs, batch inference, emails, ETL and anything slow that shouldn't block a web response.

app & tasks calling & results workers & beat canvas (workflows) config & ops gotcha most common

Verified 2026-08-24 against the official docs at docs.celeryq.dev (Celery 5.6) and the celery/celery repo. Needs a broker (Redis or RabbitMQ); a result backend is optional but required to read return values.

Outline

Define a task → call .delay() → run a worker. Add a result backend to read outputs, beat to schedule, and canvas to compose multi-step pipelines.

Define

  1. 1 · Install & app
  2. 2 · Define tasks

Call

  1. 3 · delay / apply_async
  2. 4 · Results & retries

Run

  1. 5 · Workers & queues
  2. 6 · Scheduling (beat)

Compose

  1. 7 · Signatures & chains
  2. 8 · Groups & chords

Operate

  1. 9 · Config & monitoring
  2. 10 · Gotchas
  3. Worth memorizing

Define

An app tied to a broker, and functions marked as tasks.

1Install & app5.6
2Define tasks@app.task

Call

Enqueue tasks and (optionally) collect results.

3delay / apply_asyncenqueue
4Results & retriesoutcomes

Run

Start workers, and schedule periodic tasks.

5Workers & queuesexecute
6Scheduling (beat)periodic tasks

Compose (Canvas)

Build multi-step workflows out of task signatures.

7Signatures & chainspipelines
8Groups & chordsparallel + join

Operate

Production config and monitoring.

9Config & monitoringproduction
!Common gotchasread before shipping

Worth memorizing

broker moves messages, backend stores resultsRedis/RabbitMQ broker; backend optional
@app.task then .delay()nothing runs until a worker is up
.delay() vs calling directlydelay enqueues; add(2,3) runs inline, bypassing Celery
apply_async for controlcountdown/eta, queue, priority, link callbacks
bind=True for retriesself.retry(exc=e); or autoretry_for + retry_backoff
-Q to isolate workloadsroute heavy tasks to dedicated workers
beat schedules, workers executerun exactly one beat
canvas: chain / group / chordsequential / parallel / parallel-then-callback (map-reduce)
pass ids, stay idempotent, JSON onlyat-least-once delivery; never pickle untrusted