Backend Switching Guide¶
Learn how to migrate between different backends (Gunicorn to Uvicorn, Celery to Django Tasks, etc.) with step-by-step instructions and configuration examples.
Overview¶
django-prodserver makes it easy to switch backends by simply changing configuration. This guide covers common migration scenarios and provides migration checklists.
Why Switch Backends?¶
Common reasons to switch:
Performance: Need better throughput or lower latency
Features: Require async support, WebSockets, or specific capabilities
Simplicity: Move to simpler solution for easier maintenance
Platform: Deploy to different operating system (Linux to Windows)
Cost: Reduce infrastructure dependencies
Migration Strategies¶
Strategy 1: Blue-Green Deployment¶
Deploy new backend alongside old one
Test thoroughly
Switch traffic to new backend
Keep old backend as backup
Remove old backend after stabilization
Strategy 2: Gradual Migration¶
Update configuration
Deploy to staging first
Test extensively
Deploy to production
Monitor and rollback if needed
Web Server Migrations¶
Gunicorn → Uvicorn (Adding Async Support)¶
When: You want to use async views, WebSockets, or ASGI features
Before (Gunicorn - WSGI):
PRODUCTION_PROCESSES = {
"web": {
"BACKEND": "django_prodserver.backends.servers.gunicorn.GunicornServer",
"ARGS": {
"bind": "0.0.0.0:8000",
"workers": "4",
"timeout": "60",
}
}
}
After (Uvicorn - ASGI):
PRODUCTION_PROCESSES = {
"web": {
"BACKEND": "django_prodserver.backends.servers.uvicorn.UvicornServer",
"ARGS": {
"host": "0.0.0.0",
"port": "8000",
"workers": "4",
"timeout-keep-alive": "60",
}
}
}
Migration Steps:
Install Uvicorn:
pip install uvicorn[standard]
Verify ASGI configuration:
# Ensure asgi.py exists and is correct # myproject/asgi.py import os from django.core.asgi import get_asgi_application os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'myproject.settings') application = get_asgi_application()
Update configuration (as shown above)
Test in staging:
python manage.py server web --settings=myproject.settings.staging
Deploy to production
Considerations:
ARGS mapping differs (bind → host/port)
Fewer workers needed with async (1-2 per CPU)
Monitor performance and adjust
Gunicorn → Granian (Performance Upgrade)¶
When: You want Rust-powered performance while keeping WSGI
Before (Gunicorn):
PRODUCTION_PROCESSES = {
"web": {
"BACKEND": "django_prodserver.backends.servers.gunicorn.GunicornServer",
"ARGS": {
"bind": "0.0.0.0:8000",
"workers": "4",
}
}
}
After (Granian WSGI):
PRODUCTION_PROCESSES = {
"web": {
"BACKEND": "django_prodserver.backends.servers.granian.GranianWSGIServer",
"ARGS": {
"address": "0.0.0.0",
"port": "8000",
"workers": "4",
"threads": "2",
}
}
}
Migration Steps:
Install Granian:
pip install granian
Update configuration
Tune threads (Granian-specific)
Test and deploy
Waitress → Gunicorn (Windows → Linux)¶
When: Migrating from Windows to Linux servers
Before (Waitress - Windows):
PRODUCTION_PROCESSES = {
"web": {
"BACKEND": "django_prodserver.backends.servers.waitress.WaitressServer",
"ARGS": {
"host": "0.0.0.0",
"port": "8000",
"threads": "6",
}
}
}
After (Gunicorn - Linux):
PRODUCTION_PROCESSES = {
"web": {
"BACKEND": "django_prodserver.backends.servers.gunicorn.GunicornServer",
"ARGS": {
"bind": "0.0.0.0:8000",
"workers": "4",
}
}
}
Migration Steps:
Prepare Linux environment
Install Gunicorn:
pip install gunicorn
Update configuration
Test on Linux staging server
Migrate production
Worker Migrations¶
Celery → Django Tasks (Simplification)¶
When: Reducing complexity, removing Redis/RabbitMQ dependency
Before (Celery):
# settings.py
CELERY_BROKER_URL = 'redis://localhost:6379/0'
PRODUCTION_PROCESSES = {
"worker": {
"BACKEND": "django_prodserver.backends.workers.celery.CeleryWorker",
"APP": "myproject.celery.app",
"ARGS": {
"concurrency": "4",
}
},
"beat": {
"BACKEND": "django_prodserver.backends.workers.celery.CeleryBeat",
"APP": "myproject.celery.app",
"ARGS": {}
}
}
After (Django Tasks - Django 5.1+):
# settings.py
INSTALLED_APPS = [
# ...
'django.contrib.tasks',
]
PRODUCTION_PROCESSES = {
"worker": {
"BACKEND": "django_prodserver.backends.workers.django_tasks.DjangoTasksWorker",
"ARGS": {
"processes": "4",
}
}
}
Migration Steps:
Ensure Django 5.1+:
pip install "django>=5.1"
Add to INSTALLED_APPS
Run migrations:
python manage.py migrate
Convert tasks:
# Before (Celery) from celery import shared_task @shared_task def send_email(email): # ... # After (Django Tasks) from django.contrib.tasks import task @task() def send_email(email): # ...
Update task calls (unchanged):
send_email.delay('user@example.com')
Test thoroughly (some Celery features not available)
Remove Celery and broker
Limitations:
No chains, groups, or chords
Database-backed (less performant at scale)
Simpler feature set
Django Tasks → Celery (Scaling Up)¶
When: Need distributed processing, complex workflows, or high volume
Before (Django Tasks):
PRODUCTION_PROCESSES = {
"worker": {
"BACKEND": "django_prodserver.backends.workers.django_tasks.DjangoTasksWorker",
"ARGS": {
"processes": "2",
}
}
}
After (Celery):
# Requires broker setup
CELERY_BROKER_URL = 'redis://localhost:6379/0'
PRODUCTION_PROCESSES = {
"worker": {
"BACKEND": "django_prodserver.backends.workers.celery.CeleryWorker",
"APP": "myproject.celery.app",
"ARGS": {
"concurrency": "4",
}
},
"beat": {
"BACKEND": "django_prodserver.backends.workers.celery.CeleryBeat",
"APP": "myproject.celery.app",
"ARGS": {}
}
}
Migration Steps:
Set up broker (Redis or RabbitMQ)
Install Celery:
pip install celery[redis]
Create Celery app (see Worker)
Convert tasks (decorator change)
Update configuration
Test with broker
Deploy
Celery → Django-Q2 (ORM-backed Alternative)¶
When: Want more features than Django Tasks but simpler than Celery
Before (Celery):
PRODUCTION_PROCESSES = {
"worker": {
"BACKEND": "django_prodserver.backends.workers.celery.CeleryWorker",
"APP": "myproject.celery.app",
"ARGS": {"concurrency": "4"}
}
}
After (Django-Q2):
# settings.py
INSTALLED_APPS = [
# ...
'django_q',
]
Q_CLUSTER = {
'name': 'myproject',
'workers': 4,
'timeout': 90,
}
PRODUCTION_PROCESSES = {
"worker": {
"BACKEND": "django_prodserver.backends.workers.django_q2.DjangoQ2Worker",
"ARGS": {"verbosity": "1"}
}
}
Migration Steps:
Install Django-Q2:
pip install django-q2
Add to INSTALLED_APPS
Run migrations
Convert tasks:
# Before (Celery) from celery import shared_task @shared_task def process_data(data_id): # ... # After (Django-Q2) def process_data(data_id): # Regular function, no decorator # Enqueue differently from django_q.tasks import async_task async_task('myapp.tasks.process_data', data_id)
Test
Remove Celery and broker
Migration Checklist¶
Pre-Migration¶
[ ] Read new backend documentation
[ ] Understand ARGS differences
[ ] Test in local development
[ ] Test in staging environment
[ ] Prepare rollback plan
[ ] Document configuration changes
[ ] Brief team on changes
During Migration¶
[ ] Update dependencies (requirements.txt)
[ ] Update configuration (settings.py)
[ ] Convert tasks if needed
[ ] Update environment variables
[ ] Update Docker/systemd configs
[ ] Deploy to staging
[ ] Run integration tests
[ ] Monitor performance
[ ] Fix any issues
Post-Migration¶
[ ] Deploy to production
[ ] Monitor logs and errors
[ ] Check performance metrics
[ ] Verify all features work
[ ] Update documentation
[ ] Clean up old dependencies
[ ] Remove old configuration
Testing Your Migration¶
Functionality Tests¶
# Test web server
curl http://localhost:8000/
curl http://localhost:8000/admin/
# Test task processing
python manage.py shell
>>> from myapp.tasks import my_task
>>> my_task.delay()
Performance Tests¶
# Load testing with apache bench
ab -n 1000 -c 10 http://localhost:8000/
# Monitor resource usage
top
htop
docker stats
Rollback Plan¶
Keep old configuration for quick rollback:
# settings.py
USE_NEW_BACKEND = os.getenv('USE_NEW_BACKEND', 'False') == 'True'
if USE_NEW_BACKEND:
PRODUCTION_PROCESSES = {
# New backend config
}
else:
PRODUCTION_PROCESSES = {
# Old backend config
}
Common Issues¶
ARGS Not Working¶
Problem: Configuration not being applied
Solution: Check ARGS mapping for new backend
Different backends use different argument names
Consult backend-specific documentation
Performance Regression¶
Problem: New backend is slower
Solution: Tune configuration
Adjust worker/thread counts
Check resource limits
Monitor bottlenecks
Features Missing¶
Problem: Feature worked in old backend but not new one
Solution: Check feature compatibility
Read limitations in backend documentation
Consider alternative approaches
May need to keep old backend for specific features