In
this post we go through the process of deploying a Django project
called “mysite” to production environment on an Ubuntu server.
For database PostgreSQL is used and the Django application is run
behind Nginx proxy using gunicorn.
1. The Required Packages
We use apt package manager to install the required package on Ubuntu:
$ sudo apt update $ sudo apt install python3-venv python3-dev libpq-dev postgresql postgresql-contrib nginx curl
This is packages needed by Django and Gunicorn. PostgreSQL database manager is also installed and we use it as our database for our Django project.
2. PostgreSQL Database and User
We need a PostgreSQL database and user for our Django project. First we log in as psql:
$ sudo -u postgres psql
Now we can create our needed database:
# CREATE DATABASE mysite;
Then we should create a user:
# CREATE USER mysiteuser WITH PASSWORD 'STRONGPASSWORD';
Now we set some connection parameters that is preferred for
Django:
# ALTER ROLE mysiteuser SET client_encoding TO 'utf8'; # ALTER ROLE mysiteser SET default_transaction_isolation TO 'read committed'; # ALTER ROLE mysiteuser SET timezone TO 'UTC';
Finally change the owner on the newly created database to our database user:
# ALTER DATABASE mysite OWNER TO mysiteuser;
Our database is ready for Django connection. Press CNTR+D to exit psql.
3. Clone Your Django Project from GitHub and Prepare It
We assume your project lives in a GitGub repository. First make sure that git is installed on your server:
$ sudo apt update $ sudo apt install git
Create a system user for your Django project. We will use this system user for running our Django project using Gunicorn:
$ sudo useradd -m -d /opt/mysite -U -r -s /bin/bash mysite
Log in to this new user:
$ sudo su – mysite
Make sure you are at /opt/mysite and create static and media directories for storing our Django static and media files:
$ cd /opt/mysite $ mkdir static media
The user www-data should have access to these two folders, because nginx could not access static and media file if www-data user cant access these folders. Run this command using your sudoer user later:
$ sudo usermod -a -G mysite www-data
Now clone your Django project to home directory of your system user at /opt/mysite:
$ git clone git@github.com:you/mysite.git
Note that currently you need to set up ssh login for GitHub to clone your Django repository if it is not public. You can use ssh-keygen first to create a public key and copy the generated key to your GitHub settings.
Go to the cloned repository folder and create a virtual environment and enable it:
$ cd /opt/mysite/mysite $ python3 -m venv .venv $ source .venv/bin/activate
Hopefully you have a requirements.txt file at the root of your Django project that we can use it to install required package using pip:
(.venv) $ pip install -r requirements.txt
Now install Gunicorn and PostgreSQL database driver:
(.vent) $ pip install gunicorn psycopg2-binary
We need to change some settings in our Django settings.py file:
DEBUG = False ALLOWED_HOSTS = ['your_server_domain_or_IP', 'second_domain_or_IP', . . ., 'localhost']
Next we set our database connection parameters in settings.py:
DATABASES = { 'default': { 'ENGINE': 'django.db.backends.postgresql_psycopg2', 'NAME': 'mysite', 'USER': 'mysiteuser', 'PASSWORD': 'STRONGPASSWORD', 'HOST': 'localhost', 'PORT': '5432', } }
Finally set static and media settings:
STATIC_URL = 'static/' STATIC_ROOT = '/opt/mysite/static' MEDIA_URL = 'media/' MEDIA_ROOT = '/opt/mysite/media'
This is the required settings we should set. Save the file.
Do all migrations and also create a superuser:
(.venv) $ python manage.py makemigrations (.venv) $ python manage.py migrate (.venv) $ python manage.py createsuperuser
Now you can run collectstatic command:
(.venv) $ python manage.py collectstatic
To test your Django project first make sure that if you use ufw
firewall prot 8000 connection is allowed (run by your sudoer user):
$ sudo ufw allow 8000
Now test your Django project by running the development server:
(.venv) $ python manage.py runserver 0.0.0.0:8000
Test your Django site is running by going to
http://server_domain_or_IP:8000
in
your browser.
(.venv) $ gunicorn --bind 0.0.0.0:8000 mysite.wsgi
If you can see your project home page everything is right till
now and you can exit from mysite system user using CNTR+D.
4. Create System Socket and Service
We should create a Gunicorn socket and service to listen and direct connection to our Django site. First create the socket:
$ sudo nano /etc/systemd/system/gunicorn.socket
In that file type:
[Unit] Description=gunicorn socket [Socket] ListenStream=/run/gunicorn.sock [Install] WantedBy=sockets.target
Next we should create systemd service file:
$ sudo nano /etc/systemd/system/gunicorn.service
In that file type:
[Unit] Description=gunicorn daemon Requires=gunicorn.socket After=network.target [Service] User=mysite Group=www-data WorkingDirectory=/opt/mysite/mysite ExecStart=/opt/mysite/mysite/.venv/bin/gunicorn \ --access-logfile - \ --workers 3 \ --bind unix:/run/gunicorn.sock \ mysite.wsgi:application [Install] WantedBy=multi-user.target
Now Start the Gunicorn socket:
$ sudo systemctl start gunicorn.socket $ sudo systemctl enable gunicorn.socket
Check the status of the process to find out whether it was able to start:
$ sudo systemctl status gunicorn.socket
You should receive an output like this:
● gunicorn.socket - gunicorn socket Loaded: loaded (/etc/systemd/system/gunicorn.socket; enabled; vendor preset: enabled) Active: active (running) since Sun 2024-07-21 16:11:37 UTC; 3 days ago Triggers: ● gunicorn.service Listen: /run/gunicorn.sock (Stream) CGroup: /system.slice/gunicorn.socket Jul 21 16:11:37 302477 systemd[1]: Listening on gunicorn socket.
Finally change static and media folders access rights, so www-data can access these folder, because this will not allow it to get access to static and media folders:
$ sudo usermod -a -G mysite www-data $ sudo chmod -R g+rw /opt/mysite/static $ sudo chmod -R g+rw /opt/mysite/media
5. Configure Nginx to Proxy Connections to Gunicorn
In this section we set Nginx site that passes the proxy to Gunicorn. For SSL we get certificate using Let’s Encrypt. Follow steps from Let’s Encrypt Certificate on Ubuntu and Nginx to get ssl certificate for your site.
When you are finished with steps in above guide change the nginx site configuration to:
server { listen 80; server_name www.mysite.com mysite.com; include snippets/letsencrypt.conf; return 301 https://$host$request_uri; } server { listen 443 ssl http2; server_name www.mysite.com; ssl_certificate /etc/letsencrypt/live/mysite.com/fullchain.pem; ssl_certificate_key /etc/letsencrypt/live/mysite.com/privkey.pem; ssl_trusted_certificate /etc/letsencrypt/live/mysite.com/chain.pem; include snippets/ssl.conf; include snippets/letsencrypt.conf; return 301 https://mysite.com$request_uri; } server { listen 443 ssl http2; server_name mysite.com; ssl_certificate /etc/letsencrypt/live/mysite.com/fullchain.pem; ssl_certificate_key /etc/letsencrypt/live/mysite.com/privkey.pem; ssl_trusted_certificate /etc/letsencrypt/live/mysite.com/chain.pem; include snippets/ssl.conf; include snippets/letsencrypt.conf; # . . . other code location = /favicon.ico { access_log off; log_not_found off; } location /static/ { root /opt/mysite/; } location /media/ { root /opt/mysite; } location / { include proxy_params; proxy_pass http://unix:/run/gunicorn.sock; } }
Now check if Nginx site configuration is Okay:
$ sudo nginx -t
And then restart the Nginx service:
$ sudo systemctl restart nginx
Finally remove 8000 port from allowed in ufw and enable Nginx:
$ sudo ufw delete allow 8000 $ sudo ufw allow 'Nginx Full'
Your Django project setup to production is complete. Feel free to ask any questions about any errors you may encounter during this process in the comments.