Loading...
Deploying a web application can seem daunting, but it's a manageable process with a structured...
mod_wsgi
or Nginx with Gunicorn.
Note: The core principles in this guide remain best practices for modern Django deployments.
# On Debian/Ubuntu-based systems
sudo apt update && sudo apt upgrade -y
root
is a major security risk. Create a new user with administrative privileges instead.
# Create a new user (you'll be prompted for a password)
adduser your_username
# Add the new user to the 'sudo' group
usermod -aG sudo your_username
# Log out of root and log back in as your new user
exit
ssh-keygen -t rsa -b 4096
Follow the prompts. Adding a passphrase is a good extra layer of security.
Next, copy your public key to the server. The easiest method is using ssh-copy-id
.
ssh-copy-id your_username@your_server_ip
sudo nano /etc/ssh/sshd_config
Find and modify these lines to enhance security:
PermitRootLogin no
PasswordAuthentication no
ChallengeResponseAuthentication no
UsePAM no
Restart the SSH service to apply the changes.
sudo systemctl restart ssh
๐จ Critical: Before you close your current terminal, open a new one and confirm you can log in with your SSH key. If you can't, you'll be locked out! Revert the changes if it fails.
# Deny all incoming traffic and allow all outgoing by default
sudo ufw default deny incoming
sudo ufw default allow outgoing
# CRITICAL: Allow SSH access before enabling the firewall
sudo ufw allow ssh
# Allow HTTP and HTTPS traffic for your web app
sudo ufw allow http
sudo ufw allow https
# Enable the firewall
sudo ufw enable
You can check the status at any time with sudo ufw status
.
requirements.txt
pip freeze > requirements.txt
scp
for a simple copy or git
for version-controlled projects.
scp
(Secure Copy):scp -r /path/to/local/project your_username@your_server_ip:~/
git
(Recommended):git clone your_repository_url
sudo apt install python3-pip python3-venv -y
# Navigate to your project directory
cd your_project_name
# Create a virtual environment named 'venv'
python3 -m venv venv
# Activate it
source venv/bin/activate
Your shell prompt will now be prefixed with (venv)
.
pip install -r requirements.txt
settings.py
settings.py
file for production.
# your_project/settings.py
import os
# ... other settings ...
# SECURITY WARNING: don't run with debug turned on in production!
DEBUG = False
ALLOWED_HOSTS = ['your_server_ip', 'your_domain.com']
# Define the location for all static files
STATIC_ROOT = os.path.join(BASE_DIR, 'static_root')
collectstatic
command to gather all static assets (CSS, JS, images) into the STATIC_ROOT
directory you just defined.
python manage.py collectstatic
sudo apt install postgresql postgresql-contrib -y
postgres
user to create your database and a dedicated user for your app.
sudo -u postgres psql
Inside the psql
shell, run these commands:
CREATE DATABASE myprojectdb;
CREATE USER myprojectuser WITH PASSWORD 'a_very_strong_password';
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 myprojectdb TO myprojectuser;
-- Exit the psql shell
\q
settings.py
DATABASES
setting in settings.py
with your new credentials.
# your_project/settings.py
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.postgresql',
'NAME': 'myprojectdb',
'USER': 'myprojectuser',
'PASSWORD': 'a_very_strong_password',
'HOST': 'localhost',
'PORT': '', # Default is 5432
}
}
Important: Don't hardcode passwords! We'll secure these credentials in Step 8. You'll also need to install the database adapter: pip install psycopg2-binary
.
python manage.py migrate
mod_wsgi
sudo apt install apache2 libapache2-mod-wsgi-py3 -y
sudo a2enmod wsgi
sudo nano /etc/apache2/sites-available/your_project.conf
Paste and edit the following configuration.
<VirtualHost *:80>
ServerName your_domain.com
ServerAlias www.your_domain.com
Alias /static /home/your_username/your_project_name/static_root
<Directory /home/your_username/your_project_name/static_root>
Require all granted
</Directory>
<Directory /home/your_username/your_project_name/your_project/ >
<Files wsgi.py>
Require all granted
</Files>
</Directory>
WSGIDaemonProcess your_project python-home=/home/your_username/your_project_name/venv python-path=/home/your_username/your_project_name
WSGIProcessGroup your_project
WSGIScriptAlias / /home/your_username/your_project_name/your_project/wsgi.py
</VirtualHost>
sudo a2ensite your_project.conf
sudo a2dissite 000-default.conf # Disable the default page
sudo systemctl restart apache2
sudo apt install nginx -y
pip install gunicorn # Install inside your venv
sudo nano /etc/nginx/sites-available/your_project
Paste and edit the following. This tells Nginx to serve static files directly and proxy other requests to Gunicorn.
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/your_username/your_project_name;
}
location / {
proxy_set_header Host $http_host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_pass http://unix:/run/gunicorn.sock;
}
}
sudo ln -s /etc/nginx/sites-available/your_project /etc/nginx/sites-enabled
sudo nginx -t # Test configuration for errors
sudo systemctl restart nginx
systemd
Service for Gunicorn: To ensure Gunicorn runs reliably and starts on boot, create a service file.
sudo nano /etc/systemd/system/gunicorn.service
Paste and edit the following.
[Unit]
Description=gunicorn daemon
Requires=gunicorn.socket
After=network.target
[Service]
User=your_username
Group=www-data
WorkingDirectory=/home/your_username/your_project_name
ExecStart=/home/your_username/your_project_name/venv/bin/gunicorn \
--access-logfile - \
--workers 3 \
--bind unix:/run/gunicorn.sock \
your_project.wsgi:application
[Install]
WantedBy=multi-user.target
systemd
Socket File:
sudo nano /etc/systemd/system/gunicorn.socket
[Unit]
Description=gunicorn socket
[Socket]
ListenStream=/run/gunicorn.sock
[Install]
WantedBy=sockets.target
sudo systemctl start gunicorn.socket
sudo systemctl enable gunicorn.socket
settings.py
. Use environment variables. A clean way to manage this is with a .env
file.
python-decouple
:
pip install python-decouple
.env
file in your project's root directory (next to manage.py
). Add this file to your .gitignore
immediately!
# .env file
SECRET_KEY='your-django-secret-key-goes-here'
DEBUG=False
DATABASE_URL='postgres://myprojectuser:a_very_strong_password@localhost:5432/myprojectdb'
ALLOWED_HOSTS='.your_domain.com,your_server_ip'
settings.py
to use decouple
:
# your_project/settings.py
from decouple import config, Csv
import dj_database_url
# ...
SECRET_KEY = config('SECRET_KEY')
DEBUG = config('DEBUG', default=False, cast=bool)
ALLOWED_HOSTS = config('ALLOWED_HOSTS', cast=Csv())
# Database
DATABASES = {
'default': dj_database_url.config(default=config('DATABASE_URL'))
}