Django tasks prompt A developer published a set of AI coding-agent prompts for migrating Django projects off Celery onto Django's built-in Tasks framework with Threadmill as the task backend and django-crontask replacing celery beat. The 21-step guide covers converting @app.task and @shared_task functions to @task with .enqueue(), restructuring chain/group/chord canvas workflows, mapping worker settings, and provisioning a dedicated non-evicting Redis instance, while noting Threadmill lacks a hard task timeout or rate limiter. Upgrade to Django's Task framework django-tasks Prompts Use the following prompts to instruct your AI coding agent to perform an update. Celery Replace Celery with Django's task framework django-tasks . 1. Use Threadmill as a task backend. 2. Use django-crontask to replace celery beat. Recreate every beat schedule entry and every django-celery-beat PeriodicTask row as a @cron decorated task, preserve the timezone, and decide how missed runs behave. Celery beat skips them, APScheduler applies misfire grace time . 3. Convert @app.task and @shared task functions to @task decorated functions, and replace .delay and .apply async calls with .enqueue . Reference task functions directly instead of the dotted strings passed to send task . 4. Restructure Celery canvas workflows. chain , group and chord have no equivalent. Orchestrate follow-up tasks explicitly, or collect the TaskResult objects returned by .enqueue and poll them. 5. Replace eta and countdown with task.using run after=... .enqueue . 6. Use Threadmill's retry for autoretry for , max retries and broker re-delivery by retrying on the correct HTTP exceptions and status codes. Keep true rate limits in a lock based limiter, Threadmill has no rate limiter. 7. Use AcknowledgementTimeoutError in ExponentialBackoff expected exceptions=... for tasks which must be retried on lease expiration. 8. Replace task always eager with django.tasks.backends.immediate.ImmediateBackend in tests. 9. Replace Celery task signals and result hooks with receivers of django.tasks.signals task enqueued , task started , task finished . 10. Replace AsyncResult.get and result expires with the TaskResult returned by .enqueue . Use .refresh , .status , .return value , .errors and the backend's result ttl . 11. Drop task time limit and task soft time limit . Threadmill has no hard task timeout, so enforce timeouts inside long-running tasks and keep lease ttl above the worst-case runtime. 12. Map the worker settings: worker concurrency to --workers and --threads , worker max tasks per child to --max-tasks , worker prefetch multiplier to --prefetch-count . Remove task acks late and task reject on worker lost , Threadmill always acknowledges late. 13. Add threadmill redis to the project dependencies and threadmill inspector to the dev dependency group only. 14. Use django.tasks.DEFAULT TASK QUEUE NAME and django.tasks.DEFAULT TASK BACKEND ALIAS instead of "default" literals. 15. Keep all current queue names and workers. Map every queue from task routes and the -Q flags to a TASKS alias or queue name , translate priorities from the 0 to 9 range to the -100 to 100 range, and run one manage.py threadmill worker --queues ... per queue. 16. Pick thread/process worker configurations that meet available production RAM and CPU count, and validate the worker process/thread sizing. Note that redis hiredis re-enables the GIL and costs thread parallelism. 17. Provision a dedicated, persistent Redis for the task backend. Threadmill does not support the RabbitMQ or SQS brokers, and the Redis instance must have no eviction policy set. Do not share an LRU cache instance. 18. Replace the Celery worker and beat processes with manage.py threadmill worker and manage.py crontask in every deployment config, and drain the Celery queues before the cutover. 19. Uninstall Celery, django-celery-beat and django-celery-results, remove the CELERY settings, and delete their database tables in a migration. 20. Set CRONTASK 'REDIS URL' so the scheduler runs with a Redis lock, and document instructions how to connect the Threadmill inspector to the production redis. For example: REDIS URL=rediss://xxx uv run --group=dev python manage.py threadmill inspector . 21. If the project uses django-health-check , add health check.contrib.crontask.Scheduler and health check.contrib.threadmill.Threadmill . django-tasks : https://docs.djangoproject.com/en/stable/topics/tasks/ Threadmill : https://github.com/codingjoe/threadmill django-crontask : https://github.com/codingjoe/django-crontask retry : https://github.com/codingjoe/threadmill retrying-failed-tasks django-health-check : https://codingjoe.dev/django-health-check Dramatiq Replace Dramatiq and django-dramatiq with Django's task framework django-tasks . 1. Use Threadmill as a task backend. 2. Use django-crontask to replace dramatiq-crontab. Recreate every @cron and @interval periodic task as a @cron decorated task or an APScheduler IntervalTrigger , and move DRAMATIQ CRONTAB 'REDIS URL' to CRONTASK 'REDIS URL' . 3. Convert @dramatiq.actor functions to @task decorated functions, and replace .send and .send with options calls with .enqueue . 4. Replace the delay and run after send options with task.using run after=... .enqueue . 5. Translate the broker middleware in DRAMATIQ BROKER : Retries to retry callbacks, Callbacks to django.tasks.signals , Prometheus to your metrics pipeline, and AdminMiddleware to the Threadmill inspector. AgeLimit and TimeLimit have no equivalent, so drop stale work with run after and enforce timeouts inside the task. 6. Use Threadmill's retry for max retries and broker re-delivery by retrying on the correct HTTP exceptions and status codes. Keep true rate limits in a lock based limiter, Threadmill has no replacement for RateLimiters . 7. Use AcknowledgementTimeoutError in ExponentialBackoff expected exceptions=... for tasks which must be retried on lease expiration. 8. Replace the Dramatiq test mode with django.tasks.backends.immediate.ImmediateBackend in tests. 9. Replace after process message callbacks with receivers of django.tasks.signals task enqueued , task started , task finished . 10. Replace the Dramatiq results backend and the django dramatiq admin task list with the TaskResult returned by .enqueue . Use .refresh , .status , .return value , .errors and the backend's result ttl . 11. Drop TimeLimit . Threadmill has no hard task timeout, so enforce timeouts inside long-running tasks and keep lease ttl above the worst-case runtime. 12. Map the worker settings: DRAMATIQ NPROCS and --processes to --workers , DRAMATIQ NTHREADS and --threads to --threads , and worker restarts to --max-tasks . 13. Add threadmill redis to the project dependencies and threadmill inspector to the dev dependency group only. 14. Use django.tasks.DEFAULT TASK QUEUE NAME and django.tasks.DEFAULT TASK BACKEND ALIAS instead of "default" literals. 15. Keep all current queue names and workers, map every Dramatiq queue to a TASKS alias or queue name , and run one manage.py threadmill worker --queues ... per queue. 16. Pick thread/process worker configurations that meet available production RAM and CPU count, and validate the worker process/thread sizing. Note that redis hiredis re-enables the GIL and costs thread parallelism. 17. Provision a dedicated, persistent Redis for the task backend. Threadmill does not support the RabbitMQ broker, and the Redis instance must have no eviction policy set. Do not share an LRU cache instance. 18. Replace the rundramatiq worker and the dramatiq-crontab scheduler with manage.py threadmill worker and manage.py crontask in every deployment config, and drain the Dramatiq queues before the cutover. 19. Uninstall dramatiq , django dramatiq and dramatiq crontab , remove the DRAMATIQ settings, and delete the django dramatiq.models.Task table in a migration. 20. Set CRONTASK 'REDIS URL' so the scheduler runs with a Redis lock, and document instructions how to connect the Threadmill inspector to the production redis. For example: REDIS URL=rediss://xxx uv run --group=dev python manage.py threadmill inspector . 21. If the project uses django-health-check , add health check.contrib.crontask.Scheduler and health check.contrib.threadmill.Threadmill . django-tasks : https://docs.djangoproject.com/en/stable/topics/tasks/ Threadmill : https://github.com/codingjoe/threadmill django-crontask : https://github.com/codingjoe/django-crontask retry : https://github.com/codingjoe/threadmill retrying-failed-tasks django-health-check : https://codingjoe.dev/django-health-check Django Q2 Replace Django Q2 with Django's task framework django-tasks . 1. Use Threadmill as a task backend. 2. Use django-crontask to replace the Schedule model and the cluster scheduler. Recreate every Schedule row as a @cron decorated task: translate MINUTES, HOURLY, DAILY, WEEKLY, BIWEEKLY, MONTHLY, BIMONTHLY, QUARTERLY and YEARLY to cron expressions or APScheduler triggers, preserve the timezone, and reproduce repeats with a trigger end date. 3. Convert async task calls and AsyncTask instances to @task decorated functions. Pass the function directly instead of the dotted string, and queue delayed ONCE schedules with task.using run after=... .enqueue . 4. Replace hook= and the cluster signals with receivers of django.tasks.signals task enqueued , task started , task finished or a Threadmill retry callback. 5. Replace result , fetch , result group , cached and the group helpers with the TaskResult objects returned by .enqueue : use .status , .return value , .errors and .refresh . A group becomes a list of results, and cached results map to the backend's result ttl . 6. Replace sync=True and the Q CLUSTER 'sync' test switch with django.tasks.backends.immediate.ImmediateBackend . 7. Use Threadmill's retry for max attempts and broker re-delivery by retrying based on the correct HTTP exceptions and status codes. 8. Use AcknowledgementTimeoutError in ExponentialBackoff expected exceptions=... for tasks which must be retried on lease expiration. 9. Map the remaining Q CLUSTER settings to Threadmill: name and ALT CLUSTERS to backend aliases and queue names, save limit and save limit per to result ttl , recycle and max rss to --max-tasks . There is no hard task timeout, so enforce timeouts inside long-running tasks and keep lease ttl above the worst-case runtime. ack failures disappears: failures are acknowledged and marked FAILED unless a retry callback re-queues them. 10. Add threadmill redis to the project dependencies and threadmill inspector to the dev dependency group only. 11. Use django.tasks.DEFAULT TASK QUEUE NAME and django.tasks.DEFAULT TASK BACKEND ALIAS instead of "default" literals. 12. Keep all current queue names and workers, and run one manage.py threadmill worker per queue. 13. Pick thread/process worker configurations that meet available production RAM and CPU count, and validate the worker process/thread sizing. Note that redis hiredis re-enables the GIL and costs thread parallelism. 14. Provision a dedicated, persistent Redis for the task backend. Threadmill does not support the ORM, SQS, MongoDB or IronMQ brokers, and the Redis instance must have no eviction policy set. Do not share an LRU cache instance. 15. Replace the qcluster process with manage.py threadmill worker and manage.py crontask in every deployment config. Drain the Django Q2 queues before the cutover. 16. Uninstall Django Q2, remove the Q CLUSTER setting and delete the Task , Success , Failure , Schedule and OrmQ models in a migration. 17. Set CRONTASK 'REDIS URL' so the scheduler runs with a Redis lock, and document instructions how to connect the Threadmill inspector to the production redis. For example: REDIS URL=rediss://xxx uv run --group=dev python manage.py threadmill inspector . 18. If the project uses django-health-check , add health check.contrib.crontask.Scheduler and health check.contrib.threadmill.Threadmill . django-tasks : https://docs.djangoproject.com/en/stable/topics/tasks/ Threadmill : https://github.com/codingjoe/threadmill django-crontask : https://github.com/codingjoe/django-crontask retry : https://github.com/codingjoe/threadmill retrying-failed-tasks django-health-check : https://codingjoe.dev/django-health-check