Introduction
Django is a powerful Python web framework designed for building robust web applications. This comprehensive guide will walk you through deploying a Django project in a production environment using industry best practices.
It explains how to configure Nginx as a reverse proxy, integrate S3-compatible storage for media files, and secure the deployment with proper configurations.
Prerequisites
Before starting, ensure you have:
- Basic Linux command knowledge
- A Debian-based server with root/sudo access
- An S3-compatible storage bucket (e.g. Hetzner Object Storage, or MinIO)
- A domain name (optional but recommended)
- System requirements:
- Minimum 1GB RAM
- 10GB storage space
- Python 3.8 or higher
Step 1 - System Preparation
First, let's update your system and install the necessary dependencies:
sudo apt update && sudo apt upgrade -y
sudo apt install -y python3 python3-pip python3-venv nginx
Step 2 - Django Project Setup
Let's create a clean development environment:
# Create and navigate to project directory
mkdir /home/<yourname>/django_project
cd /home/<yourname>/django_project
# Set up Python virtual environment
python3 -m venv venv
source venv/bin/activate
# Install core dependencies
pip install django python-dotenv
# Create Django project
django-admin startproject myproject .
# Initialize database
python manage.py migrate
python manage.py createsuperuser
Step 3 - Configure Django for S3 Storage
-
Install
django-storages
andboto3
:These libraries allow Django to interact with S3-compatible storage:
pip install django-storages boto3
-
Modify
settings.py
:Open
myproject/settings.py
and add the following configurations:Keep the variables for the S3 information. Those are set in the next step.
import os from dotenv import load_dotenv # Load environment variables from the .env file load_dotenv() # Add 'storages' to the INSTALLED_APPS list INSTALLED_APPS = [ # Other installed apps 'django.contrib.contenttypes', 'django.contrib.auth', 'django.contrib.admin', 'django.contrib.sessions', 'django.contrib.messages', 'django.contrib.staticfiles', 'storages', ] # Basic security and configuration settings ALLOWED_HOSTS = ["*"] DEFAULT_AUTO_FIELD = "django.db.models.BigAutoField" # Static and media file settings STATICFILES_DIRS = [BASE_DIR / "static"] STATIC_ROOT = BASE_DIR / "assets" STATIC_URL = "assets/" MEDIA_URL = "media/" # S3 configuration AWS_S3_ACCESS_KEY_ID = os.getenv("AWS_S3_ACCESS_KEY_ID") AWS_S3_SECRET_ACCESS_KEY = os.getenv("AWS_S3_SECRET_ACCESS_KEY") AWS_S3_ENDPOINT_URL = os.getenv("AWS_S3_ENDPOINT_URL") AWS_STORAGE_BUCKET_NAME = os.getenv("AWS_STORAGE_BUCKET_NAME") AWS_S3_FILE_OVERWRITE = False AWS_DEFAULT_ACL = "private" # Restrict file access AWS_S3_SIGNATURE_VERSION = "s3" # Storage settings for Django STORAGES = { "default": { "BACKEND": "storages.backends.s3.S3Storage", # Use S3 for default file storage }, "staticfiles": { "BACKEND": "django.contrib.staticfiles.storage.StaticFilesStorage", # Default storage for static files }, }
Apply the Changes
# Create a directory for static files mkdir static # Collect static files python manage.py collectstatic --noinput
-
Create a
.env
File for AWS Credentials:Create a
.env
file in the project root and add your AWS credentials:AWS_S3_ACCESS_KEY_ID=your_access_key AWS_S3_SECRET_ACCESS_KEY=your_secret_key AWS_S3_ENDPOINT_URL=https://your_endpoint_url AWS_STORAGE_BUCKET_NAME=your_bucket_name
Step 4 - Set Up Gunicorn as a WSGI Server
-
Install and Configure Gunicorn
Gunicorn will serve our Django application:
pip install gunicorn
Test Gunicorn:
gunicorn --bind 0.0.0.0:8000 myproject.wsgi
You should be able to access
http://<your_server_ip>:8000
Press
CTRL + C
to stop it. -
Create a Systemd Service for Gunicorn:
Create a service file:
sudo nano /etc/systemd/system/gunicorn.service
Add the following content:
Replace
<yourname>/django_project
with the name of your actual user and project name.[Unit] Description=Gunicorn daemon for Django project After=network.target [Service] User=www-data Group=www-data WorkingDirectory=/home/<yourname>/django_project ExecStart=/home/<yourname>/django_project/venv/bin/gunicorn --workers 3 --bind 127.0.0.1:8000 myproject.wsgi:application [Install] WantedBy=multi-user.target
Enable and start Gunicorn:
sudo systemctl daemon-reload sudo systemctl enable gunicorn sudo systemctl start gunicorn
Check to see if the Gunicorn Service is Active:
sudo systemctl status gunicorn
Click here if it failed
If you get an error that says
Changing to the requested working directory failed: Permission denied
, you may need to adjust the permissions to your user:sudo chmod o+x /home/<yourname> sudo pkill -9 gunicorn sudo systemctl restart gunicorn
You should now also get some output when you run this command:
curl -s http://127.0.0.1:8000 | head -n 7
Step 5 - Set Up Nginx as a Reverse Proxy
-
Create an Nginx Configuration File
Create a new Nginx configuration file:
sudo nano /etc/nginx/sites-available/myproject
Add the following configuration:
Replace
<yourname>/django_project
with the name of your actual user and project name, and<your_server_ip_or_domain>
with your own IP or domain.server { listen 80; listen [::]:80; server_name <your_server_ip_or_domain>; access_log /var/log/nginx/django_access.log; error_log /var/log/nginx/django_error.log; client_max_body_size 100M; # Adjust based on your needs location / { proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_pass http://127.0.0.1:8000; } # Serve static files directly through nginx location /assets/ { alias /home/<yourname>/django_project/assets/; expires 30d; add_header Cache-Control "public, no-transform"; } }
-
Enable the Configuration and Restart Nginx
sudo ln -s /etc/nginx/sites-available/myproject /etc/nginx/sites-enabled sudo nginx -t sudo systemctl restart nginx
You should be able to access
http://<your_server_ip_or_domain>
.
Step 6 - Secure Your Setup with Firewall and SSL (Optional)
Allow HTTP and HTTPS traffic:
# Install and configure UFW
sudo apt install ufw
sudo ufw default deny incoming
sudo ufw default allow outgoing
sudo ufw allow ssh
sudo ufw allow "Nginx Full"
sudo ufw enable
If you have a domain, secure it with Let's Encrypt SSL:
sudo apt install certbot python3-certbot-nginx
sudo certbot --nginx -d example.com -d www.example.com
Step 7 - File Upload Testing
Let's create a simple app to test our S3 storage integration:
python manage.py startapp upload
Add the following model (upload/models.py
) to test file uploads:
from django.db import models
class TestUpload(models.Model):
name = models.CharField(max_length=100, help_text="Enter a name for the upload")
image = models.FileField(upload_to="uploads/", help_text="Select a file to upload")
def __str__(self):
return f"Upload: {self.name}"
Register the model in your admin interface (upload/admin.py
):
from django.contrib import admin
from .models import TestUpload
@admin.register(TestUpload)
class TestUploadAdmin(admin.ModelAdmin):
list_display = ['name', 'image']
search_fields = ['name']
Now edit myproject/settings.py
and add 'upload'
in the section "INSTALLED_APPS:
INSTALLED_APPS = [
# Other installed apps
'django.contrib.contenttypes',
'django.contrib.auth',
'django.contrib.admin',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'storages',
'upload',
]
Apply the database changes:
python manage.py makemigrations
python manage.py migrate
Step 8 - Deployment Verification
Testing Your Setup
- Visit your domain or server IP in a browser
- Access the admin panel at
/admin
- Login — if you get an error, see troubleshooting below
- Try uploading a file through the TestUpload model
- Verify the file appears in your S3 bucket
Troubleshooting
-
If you encounter permission issues:
-
Set proper ownership of the project directory:
sudo chown -R www-data:www-data /home/<yourname>/django_project sudo chmod -R 755 /home/<yourname>/django_project
-
For SQLite database, set file permissions:
sudo chmod 666 /home/<yourname>/django_project/db.sqlite3
-
After making permission changes, restart Gunicorn:
sudo systemctl restart gunicorn
-
-
If static files aren't loading, run
python manage.py collectstatic
-
If uploads fail, check your S3 credentials and permissions
-
For 502 errors, verify Gunicorn is running properly
Conclusion
Congratulations! You now have a production-ready Django deployment with:
- A robust application server (Gunicorn)
- High-performance reverse proxy (Nginx)
- Scalable cloud storage solution (S3)
- Automated process management (Systemd)