Choosing a task backend
This page compares django-ox with the task queues a Django team is most likely to shortlist: django-tasks-db, huey, Celery, django-q2, dramatiq and procrastinate. Every cell about another project comes from that project's own documentation, source or issue tracker, with the link in the footnotes and the date it was read. Where a project's pages do not say, the cell says so rather than guessing.
The comparison is about fit, not ranking. A team that already runs RabbitMQ and needs to stop running tasks has a different answer from a team that wants one fewer service. The last section says where django-ox is not the right fit.
The table
| django-ox | django-tasks-db | huey | Celery | django-q2 | dramatiq | procrastinate | |
|---|---|---|---|---|---|---|---|
django.tasks backend (Django 6) |
Yes, native | Yes, native 1 | No. Issue #870 closed 2025-11-14 9 | No. Issue #10062 closed 2026-02-03 18 | Pull request #315 open since 2026-02-04, last updated 2026-05-15 26 | Not documented 29 | Not documented; has its own Django integration 33 |
| Broker needed | None. The queue is a table in your database | None. Django ORM 1 | Redis, SQLite, PostgreSQL, file or memory storage 8 | RabbitMQ, Redis or SQS (stable); Zookeeper, Kafka, Pub/Sub (experimental). SQL databases are result backends only 12. Issue #5149, PostgreSQL as a broker, open since 2018-10-25 with 95 upvotes 16 | Redis (default), IronMQ, SQS, MongoDB or Django ORM 22 | RabbitMQ or Redis 29 | None. PostgreSQL is the queue 33 |
| Transactional enqueue | Yes. Enqueue is an INSERT on your connection; it commits or rolls back with your data | Not documented 1 | Not documented 8 | No. Django's own docs name a background task as the case for on_commit() 21 |
Not documented 22 | No. Enqueue is a broker send 29 | Not documented 34 |
| Retries and backoff | Exponential backoff, MAX_ATTEMPTS, BACKOFF_INITIAL, BACKOFF_MAX; every attempt's traceback kept |
No retry option in the README or the worker flags 1 2 | retries, retry_delay, retry_backoff 8 |
autoretry_for, retry_backoff, retry_backoff_max (default 600 s), retry_jitter 11 |
max_attempts (default 0, meaning unlimited) and a retry interval; backoff not documented 23 |
Exponential backoff; max_retries default 20, min_backoff 15 s, max_backoff 7 days 29 |
Retry strategy per task 33 |
| Recurring schedules | Cron in settings; every worker dispatches, no scheduler process | None. Issue #259 open since 2026-08-10 7 | periodic_task(crontab(...)) 8 |
celery beat, a separate process; "ensure only a single scheduler is running" 14 |
Schedule model, editable in admin; cron via croniter 24 |
None built in; APScheduler recommended 30 | @app.periodic 33 |
| Priorities | -100 to 100 | Yes 3 | Yes; on Redis needs 5.0+ and PriorityRedisHuey 8 |
0 to 255 on RabbitMQ and Redis 15 | Not documented 23 | Per actor; lower number runs first 29 | Yes 33 |
| Time limit on a running task | TASK_TIMEOUT, per queue in TASK_TIMEOUTS: TaskTimeout raised inside the task at the deadline, and a worker recycle when the thread does not stop. Two caveats leave the grace backstop as the whole enforcement: an interpreter with no facility for raising an exception in another thread, and a thread a coverage tool or debugger is watching. See Production |
Not documented in the README or the worker flags 1 2 | timeout per task or per call; "a TaskTimeout is raised and returned to the caller via the result handle" 8 |
soft_time_limit raises SoftTimeLimitExceeded inside the task; time_limit terminates the process running it, which is replaced. "Time limits don't currently work on platforms that don't support the SIGUSR1 signal" 13 |
timeout, default None: "the number of seconds a worker is allowed to spend on a task before it's terminated" 23 |
time_limit per actor, default 10 minutes, raises TimeLimitExceeded. "Time limits are best-effort. They cannot cancel system calls or any function that doesn't currently hold the GIL under CPython" 29 |
Not documented on the page checked 33 |
| Worker dies mid-task | Lease renewed every LOCK_TIMEOUT / 3; a task whose lease goes stale for LOCK_TIMEOUT is put back to READY, or marked LOST when attempts are spent. See Production |
Issue #5, open since 2024-06-11: the task 'remains marked as "PROCESSING", and thus is never picked up for re-processing nor marked as completed / failed' 4 | "tasks that are mid-execution are lost and will not be retried automatically" 8 | acks_late re-delivers; the worker still acknowledges "if the child process executing the task is terminated" 11 |
Issue #327, open since 2026-05-05: worker death not reported to the monitor, MAX_ATTEMPTS ignored 27 |
Not stated on the pages checked 29 30 | Heartbeat every 10 s; jobs stay in doing until a retry_stalled_jobs periodic task you define picks them up 35 |
| Health and metrics | ox_health command, django_ox.stats, a Prometheus endpoint (/ox/metrics), structured log events, admin retry and discard |
Issue #44, container healthchecks, open since 2026-06-08 5 | Signals 8 | Flower, a separate process, with Prometheus integration 20 | qmonitor, qinfo, Stat 25 |
Prometheus middleware; not in the default middleware list 31 | Statistics via events 33 |
| Databases | PostgreSQL, SQLite and MySQL 8 tested in CI; MariaDB 10.6+ untested | Any Django database 1 | Redis, SQLite, PostgreSQL, file, memory 8 | Broker, not a database 12 | Any Django database through the ORM broker 22 | Broker, not a database 29 | PostgreSQL 13+ 33 |
| Licence | BSD 3-Clause | BSD 3-Clause 6 | MIT 10 | BSD 3-Clause 19 | MIT 28 | LGPL 3.0 32 | MIT 36 |
Async tasks: django-ox sets supports_async_task, so async def tasks
enqueue and run. Celery's most-upvoted open issue is #6552, "Support async
function", open since 2020-12-19 with 98 upvotes 17.
When not to use django-ox
- You need to stop one chosen task while it runs. django-ox bounds every
attempt with
TASK_TIMEOUT, discards a queued task and retries a failed one, but has no call that interrupts a particular running task on demand. Celery can revoke and terminate a running task, from Flower or the control API 20. - The queue must live on a different database from your models. Tasks are stored on the default database. Multi-database routing is outside the current scope, and a separate queue database would also give up the transactional enqueue.
- Throughput beyond what one database comfortably serves. The benchmarks page gives measured numbers with the method. If your workload is above them, a broker-based queue is the right tool, and the cost is the second datastore.
- Chains, groups and chords. Not in django-ox. Batches are in Oxpull Pro, a paid add-on that is not on sale yet; chains and workflows are on the Pro roadmap, undated.
- CPU-bound tasks in one process. Worker concurrency is a thread pool.
Run
ox_worker --processes N --concurrency 1for N interpreters, or pick a queue with a process pool.
Maintenance
Rows are re-checked each release. If a cell is out of date, open an issue with the link that shows it, and it will be corrected in the next release.
-
https://github.com/RealOrangeOne/django-tasks-db README, checked 2026-08-23. ↩↩↩↩↩↩
-
https://github.com/RealOrangeOne/django-tasks-db/blob/master/django_tasks_db/management/commands/db_worker.py,
add_arguments, checked 2026-08-23. ↩↩ -
https://github.com/RealOrangeOne/django-tasks-db/blob/master/django_tasks_db/backend.py,
supports_priority = True, checked 2026-08-23. ↩ -
https://github.com/RealOrangeOne/django-tasks-db/issues/5, open, checked 2026-08-23. ↩
-
https://github.com/RealOrangeOne/django-tasks-db/issues/44, open, checked 2026-08-23. ↩
-
https://github.com/RealOrangeOne/django-tasks-db, licence field, checked 2026-08-23. ↩
-
https://github.com/RealOrangeOne/django-tasks/issues/259, open, checked 2026-08-23. ↩
-
https://huey.readthedocs.io/en/latest/guide.html, checked 2026-08-23. ↩↩↩↩↩↩↩↩↩
-
https://github.com/coleifer/huey/issues/870, closed, checked 2026-08-23. ↩
-
https://github.com/coleifer/huey, licence field, checked 2026-08-23. ↩
-
https://docs.celeryq.dev/en/stable/userguide/tasks.html, checked 2026-08-23. ↩↩
-
https://docs.celeryq.dev/en/stable/getting-started/backends-and-brokers/index.html, checked 2026-08-23. ↩↩
-
https://docs.celeryq.dev/en/stable/userguide/workers.html, Time Limits, checked 2026-08-23. ↩
-
https://docs.celeryq.dev/en/stable/userguide/periodic-tasks.html, checked 2026-08-23. ↩
-
https://docs.celeryq.dev/en/stable/userguide/calling.html, checked 2026-08-23. ↩
-
https://github.com/celery/celery/issues/5149, open, checked 2026-08-23. ↩
-
https://github.com/celery/celery/issues/6552, open, checked 2026-08-23. ↩
-
https://github.com/celery/celery/issues/10062, closed, checked 2026-08-23. ↩
-
https://github.com/celery/celery/blob/main/LICENSE, checked 2026-08-23. ↩
-
https://flower.readthedocs.io/en/latest/features.html, checked 2026-08-23. ↩↩
-
https://docs.djangoproject.com/en/6.0/topics/db/transactions/#performing-actions-after-commit, checked 2026-08-23. ↩
-
https://django-q2.readthedocs.io/en/master/brokers.html, checked 2026-08-23. ↩↩↩
-
https://django-q2.readthedocs.io/en/master/configure.html, checked 2026-08-23. ↩↩↩
-
https://django-q2.readthedocs.io/en/master/schedules.html, checked 2026-08-23. ↩
-
https://django-q2.readthedocs.io/en/master/monitor.html, checked 2026-08-23. ↩
-
https://github.com/django-q2/django-q2/pull/315, open, checked 2026-08-23. ↩
-
https://github.com/django-q2/django-q2/issues/327, open, checked 2026-08-23. ↩
-
https://github.com/django-q2/django-q2/blob/master/pyproject.toml, checked 2026-08-23. ↩
-
https://dramatiq.io/guide.html, checked 2026-08-23. ↩↩↩↩↩↩↩↩
-
https://github.com/Bogdanp/dramatiq/blob/master/dramatiq/middleware/init.py,
default_middleware, andmiddleware/prometheus.py, checked 2026-08-23. ↩ -
https://github.com/Bogdanp/dramatiq, licence field, checked 2026-08-23. ↩
-
https://procrastinate.readthedocs.io/en/stable/, checked 2026-08-23. ↩↩↩↩↩↩↩↩
-
https://procrastinate.readthedocs.io/en/stable/howto/django/basic_usage.html, checked 2026-08-23. ↩
-
https://procrastinate.readthedocs.io/en/stable/howto/production/retry_stalled_jobs.html, checked 2026-08-23. ↩
-
https://github.com/procrastinate-org/procrastinate, licence field, checked 2026-08-23. ↩