Django 6.0, releasing December 3, 2025, introduces a built-in background tasks framework after 20 years – but with a deliberate catch: it doesn’t actually run tasks. Django provides the API (@task decorator, enqueue() method, TaskResult objects) but intentionally omits worker implementation, forcing developers to supply their own execution infrastructure. This isn’t a bug or a gap in the roadmap. It’s strategic product design that creates ecosystem standardization without dictating implementation.
Every Django developer deals with background tasks – sending emails, resizing images, calling external APIs. For 20 years, the ecosystem fragmented across Celery, RQ, Huey, and countless others. Each Django app picked a different solution, creating integration nightmares. Django 6.0 unifies the ecosystem with a common API while letting developers choose execution mechanisms. It’s a brilliant strategy that learned from both Flask’s lightweight approach and Django’s own fragmentation pain.
The 20-Year Wait: Fragmentation Over Implementation
Django Enhancement Proposal 14 (DEP 14) was accepted on May 29, 2024, after the Django Steering Council decided ecosystem fragmentation had become a bigger problem than lack of a first-party solution. The proposal explicitly states it “doesn’t seek to replace existing tools” but to create a “shared API contract between worker libraries and developers.” This originated from Wagtail CMS’s RFC 72, where maintainers needed background task standardization across multiple execution backends.
Jake Howard, DEP 14’s author, captured the philosophy perfectly: “Rather than replacing existing tools like Celery or RQ, the primary motivation is creating a shared API contract between worker libraries and developers.” Django avoided creating background task infrastructure for two decades because adding a complete solution would have dictated implementation and alienated the established ecosystem. Celery has millions of users. RQ is beloved for its simplicity. Adding a 13th complete solution would have made fragmentation worse, not better.
The timing is symbolic: Django 6.0 RC1 (released November 19, 2025) coincides with the 20th anniversary of Django releases. After two decades, Django proved that standardization without implementation is sometimes the right answer.
What Django Actually Ships (and What It Doesn’t)
Django 6.0 provides three components: a @task decorator for function definition, an enqueue() method for task queueing, and a backend abstraction for pluggable execution. However, what it doesn’t include: worker processes, retry logic, task scheduling, monitoring, or execution infrastructure. The two built-in backends – ImmediateBackend and DummyBackend – are explicitly “for development and testing only.”
The official documentation is blunt: “Django handles task creation and queueing, but does not provide a worker mechanism to run tasks. Execution must be managed by external infrastructure, such as a separate process or service.” This means developers must either integrate with Celery or RQ, or use Jake Howard’s django-tasks package, which provides a DatabaseBackend and a db_worker management command.
Here’s what task definition looks like:
from django.tasks import task
from django.core.mail import send_mail
@task(priority=2, queue_name="emails")
def send_welcome_email(user_id):
user = User.objects.get(id=user_id)
send_mail("Welcome!", "Thanks for signing up", None, [user.email])
# Enqueueing (transaction-safe)
with transaction.atomic():
user = User.objects.create(email="user@example.com")
transaction.on_commit(partial(send_welcome_email.enqueue, user_id=user.id))
Developers expecting a complete Celery replacement will be confused. This is an API standard, not a task queue. The value is ecosystem unification: define tasks once with @task, then plug in Celery, RQ, or custom backends without rewriting task code.
Why “Incomplete” is Brilliant Strategy
Flask’s ecosystem succeeded with RQ integration – lightweight, simple, Redis-only. Django’s ecosystem fragmented across Celery (complex but powerful), Huey (lightweight), django-background-tasks, django-rq, and others. Each Django app picked a different solution, creating integration nightmares. Reusable Django apps had to either depend on specific task libraries or avoid background tasks entirely.
By shipping an API-only solution, Django provides structure (standardized task definition) while letting the community provide implementation (execution backends). Moreover, this is the 80/20 rule in action: most Django projects need simple background tasks – send an email, resize an image, call an API. Complex workflows with task chaining, retries, and scheduled jobs? That’s the 20%.
An analysis article that trended on Hacker News on November 28 (66 points) captured this perfectly: “django.tasks will soon result in covering at least the most common 80% of use cases. Yes, its API is simple and limited, but to me that’s more a benefit rather than a fault.” Celery is overkill when all you need is email sending. Django 6.0 targets the majority.
The ecosystem comparison is stark. Old Django: App 1 uses Celery, App 2 uses RQ, App 3 uses Huey. Integration nightmare. Django 6.0: All apps use @task decorator, then plug in Celery, RQ, Huey, or custom backends. Unified API, pluggable execution. Competition on execution quality, standardization on definition.
The Production Reality: What You Need to Know
To use django.tasks in production, developers need to do three things: configure a production backend in the TASKS setting, deploy worker processes or services to execute tasks, and handle serialization constraints (JSON-only, no model instances). The built-in backends won’t work in production. Furthermore, ImmediateBackend executes tasks synchronously, defeating the entire purpose. DummyBackend doesn’t execute tasks at all – it’s for testing.
Jake Howard’s django-tasks package provides the most straightforward production path: a DatabaseBackend that stores tasks in Django’s database, executed with python manage.py db_worker. Future Celery and RQ backends will emerge, allowing @task-defined functions to execute via existing infrastructure. Consequently, the migration path is gradual: start with ImmediateBackend in development, add DatabaseBackend for simple production, migrate to Celery for complex workflows.
The critical gotcha is transaction safety. Tasks queued before database commit can fail if transactions roll back:
# BAD - task queues before commit
def create_user(request):
user = User.objects.create(email="user@example.com")
send_welcome_email.enqueue(user_id=user.id) # Fails if transaction rolls back!
# GOOD - task queues ONLY on successful commit
def create_user(request):
with transaction.atomic():
user = User.objects.create(email="user@example.com")
transaction.on_commit(partial(send_welcome_email.enqueue, user_id=user.id))
This isn’t a drop-in Celery replacement. It’s an API that requires backend integration. Setting expectations correctly prevents frustration and failed deployments.
When to Adopt (and When to Stick with Celery)
Django 6.0 Tasks makes sense for new Django projects starting fresh, simple background tasks like email or file processing, avoiding vendor lock-in to specific queue implementations, and gradual adoption paths (ImmediateBackend in dev, production backend later). However, stick with Celery if you already have Celery infrastructure deployed, need complex workflows (task chaining, automatic retries, periodic scheduling with Celery Beat), or have enterprise requirements around SLA, reliability, and built-in monitoring with Flower dashboards.
The decision matrix is straightforward. Need task chaining? Retries? Periodic scheduling? Built-in monitoring? Keep Celery. Sending emails and resizing images? django.tasks with DatabaseBackend is simpler. Celery isn’t going anywhere – future Celery backends for django.tasks will let you define tasks with @task and execute with Celery workers. Best of both worlds.
The timeline is clear: Django 6.0 final releases December 3, 2025. Backend ecosystem emergence is predicted for Q1 2026. Gradual migration of simple tasks will happen through 2026-2027. Not everyone should migrate immediately. The 80/20 rule applies: 80% of projects can benefit from simpler django.tasks, 20% need Celery’s power.
Key Takeaways
- Django 6.0 provides API standardization, not execution infrastructure. This is intentional – standardize without dictating.
- Incomplete is a feature, not a bug. Ecosystem diversity is strength when there’s a common API.
- 80% of projects benefit from simple django.tasks + DatabaseBackend. Complex workflows are the minority use case.
- Celery isn’t being replaced – it’s being integrated. Future Celery backends will let you use
@taskwith Celery execution. - Production-ready in 5 days. Django 6.0 final release is December 3, 2025.
After 20 years, Django proves that sometimes the best solution doesn’t solve the problem – it standardizes it. Explore django.tasks for new projects. Gradual migration for existing ones. The future is pluggable backends with unified APIs.









