ECS Fargate · Django producer · Redis broker · Worker consumer · Beat scheduler
.delay() or .apply_async() to enqueue work.send_message.delay(payload)Celery() instance. Both this container and the worker container import the same app object. Config comes from Django settings via app.config_from_object().
celery worker. It only calls tasks. The Celery app object is imported for task registration, not execution.
celery (default)RPUSH / BLPOP
high_prioritylow_priorityBLPOP — blocks until item available, then ACKs on completion. No at-least-once guarantee without acks_late=True.
.delay() → Kombu serializes task to JSON → RPUSH to Redis LIST. Task ID returned immediately (UUID).MainProcess runs BLPOP on queue. Blocks (timeout configurable). Task message deserialized from Kombu envelope.@app.task function runs. Django ORM works — each subprocess has its own DB connection pool.result_backend set: outcome stored in Redis/Postgres under task ID. Caller can poll AsyncResult(task_id).get() — but this blocks, so do it async.self.retry(exc=e, countdown=60) re-enqueues with incremented retry counter. After max_retries, task fails — no native DLQ in Redis, implement manually or use SQS.| Pool | Mechanism | Best For | Django ORM? |
|---|---|---|---|
| prefork | os.fork() — N subprocesses | CPU-bound, default | Yes — each worker has own conn |
| gevent | greenlets (cooperative) | I/O-bound (HTTP calls) | Careful — shared conn pool |
| eventlet | greenlets | I/O-bound | Same caveat as gevent |
| threads | threading.Thread | GIL-aware I/O | Yes — thread-local conns |
| solo | single-process | Debug only | Yes |
CONN_MAX_AGE=0 or handle worker_process_init signal to close inherited connections before children open their own.
celery -A myapp worker -c 4 -Q default,high@app.task decorated functions.
CMD differs in the ECS task definition.
CMD gunicorn ...CMD celery worker ...
celery inspect pingcelery[redis] health probe via a sidecar / custom CMD.
celery -A myapp beat -s /tmp/celerybeat-scheduleCELERY_BEAT_SCHEDULE, enqueues tasks on cron/interval. Runs as a single process — only ever 1 replica.
result_expires). If you don't poll results, skip this — it adds overhead. For fire-and-forget tasks, disable entirely.