It expands your short outline into a detailed, structured, and professional article — suitable for technical blogs, documentation, or learning materials.
No icons or emojis are used. It includes clear headings, subheadings, explanations, and code.
Introduction
Deploying a Django application to production is one of the most important milestones in any web project. After developing and testing your app locally, the next challenge is to serve it to real users over the internet efficiently and securely.
In production, Django’s built-in development server (python manage.py runserver) is not suitable. It’s intended for local testing, not for handling high traffic, concurrency, or security concerns. For real-world deployment, the recommended setup combines two robust tools: Gunicorn and Nginx.
- Gunicorn is a Python WSGI HTTP server that serves Django applications.
- Nginx is a powerful, high-performance web server and reverse proxy that handles client requests, static files, and load balancing.
Together, Gunicorn and Nginx form a proven stack that powers millions of production sites.
In this post, we’ll cover every step — from installation to optimization — for deploying Django with Gunicorn and Nginx.
1. Understanding the Deployment Architecture
Before we begin, it’s essential to understand how these components interact.
- Client (Browser/User) sends an HTTP request to your server.
- Nginx receives the request on port 80 (or 443 for HTTPS).
- Nginx forwards the request to Gunicorn, which runs your Django app.
- Gunicorn processes the request through Django’s WSGI interface.
- Django returns the response to Gunicorn.
- Gunicorn sends the response back to Nginx.
- Nginx finally delivers the response to the client.
This layered approach provides performance, flexibility, and security benefits:
- Nginx can serve static and media files directly.
- Gunicorn focuses solely on running your Django application.
- Both can be restarted or scaled independently.
2. Preparing the Server Environment
Before installing Gunicorn or Nginx, make sure your server environment is ready.
Step 1: Update Your System
If you’re on Ubuntu or Debian-based systems:
sudo apt update
sudo apt upgrade -y
Step 2: Install Python and Virtual Environment Tools
Django projects should always run inside a virtual environment to isolate dependencies.
sudo apt install python3-pip python3-venv -y
Create and activate your virtual environment:
mkdir /home/ubuntu/django_project
cd /home/ubuntu/django_project
python3 -m venv venv
source venv/bin/activate
Step 3: Install Django and Dependencies
pip install django gunicorn
If your project already exists, clone it from your repository:
git clone https://github.com/yourusername/yourproject.git
cd yourproject
pip install -r requirements.txt
3. Configuring Django for Production
Before deployment, adjust a few Django settings to make your app production-ready.
Step 1: Update ALLOWED_HOSTS
In settings.py:
ALLOWED_HOSTS = ['yourdomain.com', 'www.yourdomain.com', 'your.server.ip']
This ensures Django only serves requests from your actual domain or IP.
Step 2: Disable Debug Mode
Set:
DEBUG = False
Leaving DEBUG=True in production exposes sensitive information in error pages.
Step 3: Collect Static Files
In production, Django doesn’t serve static files automatically. Run:
python manage.py collectstatic
This command gathers all static assets into a single directory (by default STATIC_ROOT), ready for Nginx to serve efficiently.
4. Installing and Running Gunicorn
Now that Django is configured, let’s set up Gunicorn to serve your application.
Step 1: Install Gunicorn
If you haven’t already:
pip install gunicorn
Step 2: Test Gunicorn Manually
Navigate to your Django project directory (where manage.py is located) and run:
gunicorn projectname.wsgi
Replace projectname with your actual Django project’s name.
By default, Gunicorn will run on port 8000. Visit your server’s IP in a browser:
http://your_server_ip:8000
If you see your Django app, Gunicorn is working.
Press Ctrl + C to stop it.
5. Creating a Gunicorn Systemd Service
Instead of running Gunicorn manually, we’ll create a systemd service to manage it as a background process. This ensures Gunicorn starts automatically when the server boots and can be easily restarted.
Step 1: Create the Service File
Create a new file:
sudo nano /etc/systemd/system/gunicorn.service
Add the following configuration (adjust paths as needed):
[Unit]
Description=Gunicorn Daemon for Django Project
After=network.target
[Service]
User=ubuntu
Group=www-data
WorkingDirectory=/home/ubuntu/django_project/yourproject
ExecStart=/home/ubuntu/django_project/venv/bin/gunicorn --workers 3 --bind unix:/home/ubuntu/django_project/yourproject.sock projectname.wsgi:application
[Install]
WantedBy=multi-user.target
Step 2: Start and Enable the Service
sudo systemctl start gunicorn
sudo systemctl enable gunicorn
Check the status:
sudo systemctl status gunicorn
If it’s running correctly, Gunicorn is now serving your Django app through a Unix socket (.sock file) instead of a TCP port.
6. Installing and Configuring Nginx
Next, we’ll configure Nginx as a reverse proxy to forward web traffic to Gunicorn.
Step 1: Install Nginx
sudo apt install nginx -y
Step 2: Configure Nginx Server Block
Create a new configuration file for your site:
sudo nano /etc/nginx/sites-available/yourproject
Add this configuration:
server {
listen 80;
server_name yourdomain.com www.yourdomain.com;
location = /favicon.ico { access_log off; log_not_found off; }
location /static/ {
root /home/ubuntu/django_project/yourproject;
}
location / {
include proxy_params;
proxy_pass http://unix:/home/ubuntu/django_project/yourproject.sock;
}
}
Step 3: Enable the Configuration
Link it to sites-enabled:
sudo ln -s /etc/nginx/sites-available/yourproject /etc/nginx/sites-enabled
Test Nginx configuration:
sudo nginx -t
If no errors appear, reload Nginx:
sudo systemctl restart nginx
Your Django app should now be accessible through your domain name.
7. Adjusting Firewall Rules
If you’re using Ubuntu’s UFW firewall, allow Nginx to handle web traffic.
Check available profiles:
sudo ufw app list
You should see something like:
Available applications:
Nginx Full
Nginx HTTP
Nginx HTTPS
Allow Nginx Full:
sudo ufw allow 'Nginx Full'
sudo ufw enable
sudo ufw status
Now port 80 (HTTP) and port 443 (HTTPS, later for SSL) are open.
8. Setting Up Domain and DNS
For your site to be accessible by domain name, you need to:
- Register a domain (for example, through Namecheap or Google Domains).
- Point the domain’s A record to your server’s IP address.
Example DNS record:
Type: A
Host: @
Value: your_server_ip
TTL: 3600
Once propagated, visiting http://yourdomain.com should load your Django app via Nginx and Gunicorn.
9. Configuring HTTPS with Let’s Encrypt
Security is crucial in production. Using HTTPS protects data in transit and boosts SEO rankings. We’ll use Certbot, a free SSL certificate generator from Let’s Encrypt.
Step 1: Install Certbot
sudo apt install certbot python3-certbot-nginx -y
Step 2: Obtain and Install Certificate
Run the following command:
sudo certbot --nginx -d yourdomain.com -d www.yourdomain.com
Follow the interactive prompts. Once complete, Certbot automatically updates your Nginx configuration with SSL support.
Step 3: Auto-Renew Certificates
Let’s Encrypt certificates expire every 90 days. Add a cron job for automatic renewal:
sudo systemctl enable certbot.timer
sudo systemctl start certbot.timer
You can manually test renewal with:
sudo certbot renew --dry-run
Now your site is accessible over HTTPS securely.
10. Testing the Full Deployment
At this point, your Django app runs behind Gunicorn and Nginx with HTTPS enabled.
Step 1: Check Gunicorn
sudo systemctl status gunicorn
Step 2: Check Nginx
sudo systemctl status nginx
Step 3: Visit the Website
Open your browser and navigate to:
https://yourdomain.com
You should see your Django application live with a secure padlock symbol.
11. Common Deployment Troubleshooting
Even small misconfigurations can cause issues during deployment. Let’s look at some frequent problems and solutions.
Problem 1: 502 Bad Gateway
Cause: Nginx cannot connect to Gunicorn.
Fix:
- Ensure Gunicorn is running.
- Verify the socket path in Nginx and Gunicorn configurations matches exactly.
- Check file permissions for the socket file.
Problem 2: 403 Forbidden
Cause: Nginx doesn’t have permission to access static or media files.
Fix:
Ensure directories are readable by the www-data group:
sudo chown -R :www-data /home/ubuntu/django_project/yourproject
sudo chmod -R 755 /home/ubuntu/django_project/yourproject
Problem 3: Gunicorn Fails to Start
Check logs:
sudo journalctl -u gunicorn
Look for syntax errors, incorrect paths, or missing modules.
Problem 4: Static Files Not Loading
Confirm your Nginx static block matches the STATIC_ROOT directory from settings.py.
12. Scaling and Performance Optimization
Once your application is live, you can improve scalability and performance.
Use Multiple Gunicorn Workers
In your Gunicorn service file, adjust:
--workers 3
The general rule is (2 x CPU cores) + 1 workers for optimal concurrency.
Enable Gzip Compression in Nginx
Add to your Nginx config:
gzip on;
gzip_types text/plain text/css application/json application/javascript text/xml application/xml+rss;
gzip_min_length 256;
Cache Static Files
You can instruct browsers to cache static assets for faster loading:
location /static/ {
root /home/ubuntu/django_project/yourproject;
expires 30d;
add_header Cache-Control "public";
}
Use a Process Manager
Tools like Supervisor or systemd (as used above) ensure Gunicorn automatically restarts if it crashes.
13. Deployment Automation (Optional)
Manual setup is fine for learning, but in larger projects, automation tools simplify deployment.
Options include:
- Fabric – Python-based remote execution.
- Ansible – Infrastructure as code automation.
- Docker + Docker Compose – Containerized deployment.
- GitHub Actions or Jenkins – Continuous Integration/Continuous Deployment (CI/CD).
For instance, a Docker-based deployment might include:
- Gunicorn running in a container.
- Nginx as a reverse proxy container.
- PostgreSQL or MySQL database container.
Automation reduces human error and allows consistent deployments across environments.
14. Monitoring and Logging
Production apps need monitoring for stability and debugging.
View Logs
- Gunicorn logs:
sudo journalctl -u gunicorn - Nginx logs:
/var/log/nginx/access.log /var/log/nginx/error.log
Set Up Monitoring Tools
- Prometheus + Grafana: Server and app metrics.
- Sentry: Real-time error tracking for Django.
- UptimeRobot: External uptime monitoring.
These tools ensure you know when something breaks before users do.
15. Deployment Checklist
Before calling your deployment complete, review this checklist:
- Django
DEBUG = False - Proper
ALLOWED_HOSTS - Static files collected
- Gunicorn service configured
- Nginx reverse proxy configured
- SSL certificates installed
- UFW firewall enabled
- Logs tested
- Automatic renewal for certificates enabled
- Backup strategy in place
Meeting these ensures a secure, reliable production environment.
16. Why Use Gunicorn and Nginx Together
You might wonder: why not use Gunicorn alone?
- Gunicorn alone: Handles Python requests well but isn’t optimized for static files, buffering, or SSL termination.
- Nginx alone: Excellent web server, but it cannot directly run Django code.
By combining both:
- Nginx serves static and media files directly.
- Gunicorn executes Python code efficiently.
- Nginx handles SSL, caching, and client load balancing.
This layered approach provides speed, security, and reliability.
17. Upgrading or Restarting Services
Whenever you make changes, restart both services:
sudo systemctl daemon-reload
sudo systemctl restart gunicorn
sudo systemctl restart nginx
To check for errors:
sudo systemctl status gunicorn
sudo nginx -t
18. Backups and Recovery
Regular backups are vital for production reliability.
Database Backup
For PostgreSQL:
pg_dump dbname > backup.sql
For SQLite:
cp db.sqlite3 backup.sqlite3
Static Files Backup
tar -czf static_backup.tar.gz /home/ubuntu/django_project/yourproject/static/
Automate backups using cron jobs to ensure disaster recovery.
19. Future Enhancements
After mastering the Gunicorn + Nginx setup, consider adding:
- Celery for asynchronous task processing.
- Redis for caching and session storage.
- Load balancing across multiple Gunicorn workers using Nginx upstream blocks.
- CDN integration for faster static delivery worldwide.
These tools extend performance and scalability as your traffic grows.
Leave a Reply