diff --git a/tutorials/deploy-django-with-nginx-and-s3-storage/01.en.md b/tutorials/deploy-django-with-nginx-and-s3-storage/01.en.md new file mode 100644 index 000000000..94438b10d --- /dev/null +++ b/tutorials/deploy-django-with-nginx-and-s3-storage/01.en.md @@ -0,0 +1,439 @@ +--- +SPDX-License-Identifier: MIT +path: "/tutorials/deploy-django-with-nginx-and-s3-storage" +slug: "deploy-django-with-nginx-and-s3-storage" +date: "2025-02-17" +title: "Deploy Django with Nginx and S3 Storage" +short_description: "This tutorial covers the steps to deploy a Django application with Nginx as a reverse proxy and S3-compatible storage for handling static and media files." +tags: ["Cloud", "Debian", "Django", "Nginx", "Storage", "S3", "Object Storage"] +author: "Mohsen Nasiri" +author_link: "https://github.com/leaked" +author_img: "https://avatars.githubusercontent.com/u/43057475" +author_description: "Software Engineer and open-source enthusiast." +language: "en" +available_languages: ["en"] +header_img: "header-7" +cta: "cloud" +--- + +## 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](https://www.hetzner.com/storage/object-storage/), or [MinIO](https://community.hetzner.com/tutorials/how-to-install-minio-on-debian)) +- 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: + +```shell +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: + +```shell +# Create and navigate to project directory +mkdir /home//django_project +cd /home//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 + +1. Install `django-storages` and `boto3`: + + These libraries allow Django to interact with S3-compatible storage: + + ```shell + pip install django-storages boto3 + ``` + +2. 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. + + ```python + 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 + + ```shell + # Create a directory for static files + mkdir static + # Collect static files + python manage.py collectstatic --noinput + ``` + +3. Create a `.env` File for AWS Credentials: + + Create a `.env` file in the project root and add your AWS credentials: + + ```ini + 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 + +1. Install and Configure Gunicorn + + Gunicorn will serve our Django application: + + ```shell + pip install gunicorn + ``` + + Test Gunicorn: + + ```shell + gunicorn --bind 0.0.0.0:8000 myproject.wsgi + ``` + + You should be able to access `http://:8000` + + Press `CTRL + C` to stop it. + +2. Create a Systemd Service for Gunicorn: + + Create a service file: + + ```shell + sudo nano /etc/systemd/system/gunicorn.service + ``` + + Add the following content: + + > Replace `/django_project` with the name of your actual user and project name. + + ```ini + [Unit] + Description=Gunicorn daemon for Django project + After=network.target + + [Service] + User=www-data + Group=www-data + WorkingDirectory=/home//django_project + ExecStart=/home//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: + + ```shell + sudo systemctl daemon-reload + sudo systemctl enable gunicorn + sudo systemctl start gunicorn + ``` + + Check to see if the Gunicorn Service is Active: + + ```shell + 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: + ```bash + sudo chmod o+x /home/ + sudo pkill -9 gunicorn + sudo systemctl restart gunicorn + ``` +
+
+ + You should now also get some output when you run this command: + + ```shellsession + curl -s http://127.0.0.1:8000 | head -n 7 + ``` + +## Step 5 - Set Up Nginx as a Reverse Proxy + +1. Create an Nginx Configuration File + + Create a new Nginx configuration file: + + ```shell + sudo nano /etc/nginx/sites-available/myproject + ``` + + Add the following configuration: + + > Replace `/django_project` with the name of your actual user and project name, and `` with your own IP or domain. + + ```nginx + server { + listen 80; + listen [::]:80; + server_name ; + + 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//django_project/assets/; + expires 30d; + add_header Cache-Control "public, no-transform"; + } + } + ``` + +2. Enable the Configuration and Restart Nginx + + ```shell + 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://`. + +## Step 6 - Secure Your Setup with Firewall and SSL (Optional) + +Allow HTTP and HTTPS traffic: + +```shell +# 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: + +```shell +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: + +```shell +python manage.py startapp upload +``` + +Add the following model (`upload/models.py`) to test file uploads: + +```python +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`): + +```python +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: + +```python +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: + +```shell +python manage.py makemigrations +python manage.py migrate +``` + +## Step 8 - Deployment Verification + +### Testing Your Setup + +1. Visit your domain or server IP in a browser +2. Access the admin panel at `/admin` +3. Login — if you get an error, see troubleshooting below +3. Try uploading a file through the TestUpload model +4. Verify the file appears in your S3 bucket + +### Troubleshooting + +- If you encounter permission issues: + + 1. Set proper ownership of the project directory: + + ```bash + sudo chown -R www-data:www-data /home//django_project + sudo chmod -R 755 /home//django_project + ``` + + 2. For SQLite database, set file permissions: + + ```bash + sudo chmod 666 /home//django_project/db.sqlite3 + ``` + + 4. After making permission changes, restart Gunicorn: + + ```bash + 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) + +##### License: MIT + +