How to Deploy a Django App to Production in 2025
Complete production-ready Django deployment guide. From zero to production with Gunicorn, Nginx, PostgreSQL, SSL, monitoring, and auto-scaling.
What You'll Learn:
- ✓ Server setup on Vultr/DigitalOcean (with $500 free credits)
- ✓ Deploy your Django app with Gunicorn & systemd
- ✓ Configure Nginx as reverse proxy
- ✓ Set up PostgreSQL database
- ✓ Configure static files & media storage
- ✓ Set up free SSL with Let's Encrypt
- ✓ Implement monitoring and logging
- ✓ Set up CI/CD with GitHub Actions
- ✓ Scale horizontally with load balancing
Prerequisites
Before we begin, make sure you have:
- A Django app ready to deploy (Django 4.2+ or 5.0+)
- Your code in a Git repository (GitHub, GitLab, etc.)
- A domain name (optional but recommended)
- Basic command line knowledge
- Familiarity with Django settings and migrations
Step 1: Choose Your Hosting Provider
You have three main options for Django hosting. I'll show you how to deploy on all three, but I recommend starting with Vultr or DigitalOcean for the best balance of simplicity, performance, and cost.
Step 2: Create Your Server
For Vultr Users:
- Click the button above to get your $300 credits
- Click "Deploy +" → "Deploy New Server"
- Choose Ubuntu 24.04 LTS
- Select plan: $6/mo (1 vCPU, 1GB RAM) for small apps, $12/mo (1 vCPU, 2GB RAM) for production
- Choose location closest to your users
- Add SSH key or set root password
- Click "Deploy Now"
For DigitalOcean Users:
- Click the button above to get your $200 credits
- Click "Create" → "Droplets"
- Choose Ubuntu 24.04 LTS
- Select plan: Basic $6/mo (1 vCPU, 1GB RAM) for small apps, $12/mo (2GB RAM) for production
- Choose datacenter region
- Add SSH key or set root password
- Click "Create Droplet"
💡 Pro Tip:
For production Django apps, I recommend at least 2GB RAM ($12/mo) to handle PostgreSQL, Nginx, and Gunicorn comfortably. You can start with 1GB and upgrade later if needed.
Step 3: Initial Server Setup
SSH into your server:
ssh root@your_server_ipUpdate System Packages
apt update && apt upgrade -yInstall Required Packages
# Install Python, pip, and essential tools
apt install python3 python3-pip python3-venv python3-dev -y
# Install PostgreSQL
apt install postgresql postgresql-contrib -y
# Install Nginx
apt install nginx -y
# Install system dependencies for common Python packages
apt install build-essential libpq-dev libssl-dev libffi-dev -y
# Install Git
apt install git -yCreate a Deployment User (Security Best Practice)
# Create user
adduser django --disabled-password --gecos ""
# Add to sudo group
usermod -aG sudo django
# Switch to django user
su - djangoStep 4: Set Up PostgreSQL Database
Switch to postgres user and create database:
# Switch to postgres user
sudo -u postgres psql
# In PostgreSQL shell, run these commands:
CREATE DATABASE myproject;
CREATE USER myprojectuser WITH PASSWORD 'strong_password_here';
ALTER ROLE myprojectuser SET client_encoding TO 'utf8';
ALTER ROLE myprojectuser SET default_transaction_isolation TO 'read committed';
ALTER ROLE myprojectuser SET timezone TO 'UTC';
GRANT ALL PRIVILEGES ON DATABASE myproject TO myprojectuser;
\qStep 5: Clone Your Django Project
# Clone your repository
cd /home/django
git clone https://github.com/yourusername/yourproject.git
cd yourproject
# Create virtual environment
python3 -m venv venv
source venv/bin/activate
# Install dependencies
pip install --upgrade pip
pip install -r requirements.txt
pip install gunicorn psycopg2-binaryStep 6: Configure Django Settings for Production
Create a production settings file or update your existing settings.py:
# settings.py or settings/production.py
import os
from pathlib import Path
# SECURITY WARNING: keep the secret key used in production secret!
SECRET_KEY = os.environ.get('DJANGO_SECRET_KEY')
# SECURITY WARNING: don't run with debug turned on in production!
DEBUG = False
ALLOWED_HOSTS = ['your-domain.com', 'www.your-domain.com', 'your_server_ip']
# Database
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.postgresql',
'NAME': 'myproject',
'USER': 'myprojectuser',
'PASSWORD': os.environ.get('DB_PASSWORD'),
'HOST': 'localhost',
'PORT': '5432',
}
}
# Static files (CSS, JavaScript, Images)
STATIC_URL = '/static/'
STATIC_ROOT = os.path.join(BASE_DIR, 'staticfiles')
# Media files
MEDIA_URL = '/media/'
MEDIA_ROOT = os.path.join(BASE_DIR, 'media')
# Security settings
SECURE_SSL_REDIRECT = True
SESSION_COOKIE_SECURE = True
CSRF_COOKIE_SECURE = True
SECURE_BROWSER_XSS_FILTER = True
SECURE_CONTENT_TYPE_NOSNIFF = True
X_FRAME_OPTIONS = 'DENY'Set Environment Variables
# Create .env file
nano /home/django/yourproject/.env
# Add these variables:
DJANGO_SECRET_KEY=your_secret_key_here
DB_PASSWORD=strong_password_here
DJANGO_SETTINGS_MODULE=yourproject.settings.productionRun Migrations and Collect Static Files
# Make sure you're in virtual environment
source venv/bin/activate
# Load environment variables
export $(cat .env | xargs)
# Run migrations
python manage.py migrate
# Create superuser
python manage.py createsuperuser
# Collect static files
python manage.py collectstatic --no-inputStep 7: Configure Gunicorn
Test Gunicorn
# Test Gunicorn (replace 'yourproject' with your project name)
gunicorn --bind 0.0.0.0:8000 yourproject.wsgi:application
# Press Ctrl+C to stop after testingCreate Gunicorn systemd Service
sudo nano /etc/systemd/system/gunicorn.serviceAdd this configuration:
[Unit]
Description=gunicorn daemon for Django project
After=network.target
[Service]
User=django
Group=www-data
WorkingDirectory=/home/django/yourproject
EnvironmentFile=/home/django/yourproject/.env
ExecStart=/home/django/yourproject/venv/bin/gunicorn \
--access-logfile - \
--workers 3 \
--bind unix:/home/django/yourproject/gunicorn.sock \
yourproject.wsgi:application
[Install]
WantedBy=multi-user.targetStart and Enable Gunicorn
sudo systemctl start gunicorn
sudo systemctl enable gunicorn
sudo systemctl status gunicornStep 8: Configure Nginx
Create Nginx configuration:
sudo nano /etc/nginx/sites-available/yourprojectAdd this configuration:
server {
listen 80;
server_name your-domain.com www.your-domain.com;
location = /favicon.ico { access_log off; log_not_found off; }
location /static/ {
root /home/django/yourproject;
}
location /media/ {
root /home/django/yourproject;
}
location / {
include proxy_params;
proxy_pass http://unix:/home/django/yourproject/gunicorn.sock;
}
}Enable Nginx Site
# Create symlink
sudo ln -s /etc/nginx/sites-available/yourproject /etc/nginx/sites-enabled/
# Test configuration
sudo nginx -t
# Restart Nginx
sudo systemctl restart nginx
# Allow Nginx through firewall
sudo ufw allow 'Nginx Full'Step 9: Set Up SSL with Let's Encrypt
# Install Certbot
sudo apt install certbot python3-certbot-nginx -y
# Get SSL certificate
sudo certbot --nginx -d your-domain.com -d www.your-domain.com
# Test auto-renewal
sudo certbot renew --dry-run🎉 Success!
Your Django app is now live at https://your-domain.com with free SSL!
Step 10: Set Up Monitoring & Logging
Configure Django Logging
Add to settings.py:
LOGGING = {
'version': 1,
'disable_existing_loggers': False,
'handlers': {
'file': {
'level': 'ERROR',
'class': 'logging.FileHandler',
'filename': '/home/django/yourproject/logs/django.log',
},
},
'loggers': {
'django': {
'handlers': ['file'],
'level': 'ERROR',
'propagate': True,
},
},
}Monitor Application Logs
# View Gunicorn logs
sudo journalctl -u gunicorn -f
# View Nginx access logs
sudo tail -f /var/log/nginx/access.log
# View Nginx error logs
sudo tail -f /var/log/nginx/error.logStep 11: Set Up CI/CD with GitHub Actions
Create .github/workflows/deploy.yml in your repository:
name: Deploy to Production
on:
push:
branches: [ main ]
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- name: Deploy to server
uses: appleboy/ssh-action@master
with:
host: ${{ secrets.SERVER_IP }}
username: django
key: ${{ secrets.SSH_PRIVATE_KEY }}
script: |
cd /home/django/yourproject
git pull origin main
source venv/bin/activate
pip install -r requirements.txt
python manage.py migrate
python manage.py collectstatic --no-input
sudo systemctl restart gunicorn
sudo systemctl restart nginxPerformance Optimization Tips
1. Enable Database Connection Pooling
pip install django-db-pool
# In settings.py
DATABASES = {
'default': {
'ENGINE': 'django_db_pool.backends.postgresql',
# ... rest of config
'POOL_OPTIONS': {
'MAX_SIZE': 20,
}
}
}2. Set Up Redis for Caching
# Install Redis
sudo apt install redis-server -y
pip install django-redis
# In settings.py
CACHES = {
"default": {
"BACKEND": "django_redis.cache.RedisCache",
"LOCATION": "redis://127.0.0.1:6379/1",
"OPTIONS": {
"CLIENT_CLASS": "django_redis.client.DefaultClient",
}
}
}3. Optimize Gunicorn Workers
Rule of thumb: (2 × CPU cores) + 1
# For 2 CPU cores:
--workers 5
# For 4 CPU cores:
--workers 9Scaling Your Django App
Vertical Scaling (Easiest)
Upgrade your server size:
- 1GB RAM → 2GB RAM: $6/mo → $12/mo
- 2GB RAM → 4GB RAM: $12/mo → $24/mo
- 4GB RAM → 8GB RAM: $24/mo → $48/mo
Horizontal Scaling (For High Traffic)
- Move PostgreSQL to a separate managed database
- Set up Redis for session storage
- Use object storage (S3, Spaces) for media files
- Deploy multiple app servers behind a load balancer
- Use CDN for static files
Common Issues & Solutions
502 Bad Gateway Error
# Check if Gunicorn is running
sudo systemctl status gunicorn
# Check socket file permissions
ls -l /home/django/yourproject/gunicorn.sock
# Check Nginx error logs
sudo tail -f /var/log/nginx/error.logStatic Files Not Loading
# Re-collect static files
python manage.py collectstatic --clear --no-input
# Check permissions
sudo chown -R django:www-data /home/django/yourproject/staticfiles
# Verify Nginx configuration
sudo nginx -tDatabase Connection Errors
# Check PostgreSQL is running
sudo systemctl status postgresql
# Test database connection
sudo -u postgres psql -c "\l"
# Verify database credentials in .env fileCost Comparison
| Configuration | Vultr | DigitalOcean | Best For |
|---|---|---|---|
| 1GB RAM, 1 vCPU | $6/mo | $6/mo | Small apps, testing |
| 2GB RAM, 1 vCPU | $12/mo | $12/mo | Most Django apps |
| 4GB RAM, 2 vCPU | $24/mo | $24/mo | High traffic apps |
| 8GB RAM, 4 vCPU | $48/mo | $48/mo | Enterprise apps |
💰 Save Money:
With $300 in Vultr credits, you can run a production Django app (2GB RAM) for FREE for 25 months! That's over 2 years of free hosting.
Conclusion
You now have a production-ready Django deployment with:
- ✓ Gunicorn for serving your Django app
- ✓ Nginx as a reverse proxy
- ✓ PostgreSQL database
- ✓ Free SSL certificate
- ✓ Systemd for process management
- ✓ Monitoring and logging
- ✓ CI/CD pipeline
This setup will handle thousands of requests per day. For higher traffic, follow the horizontal scaling steps above.
Next Steps:
- Set up database backups with automated snapshots
- Implement application monitoring (Sentry, New Relic)
- Configure email sending (SendGrid, Mailgun)
- Add Celery for background tasks
- Set up staging environment
About this guide: This tutorial is updated for 2025 with the latest Django, Ubuntu, and deployment best practices. All commands have been tested on Ubuntu 24.04 LTS with Django 5.0.
Need help? Check out our other guides: