In today’s tutorial, we will go through the process of deploying a Django project with Apache and mod_wsgi. This will also cover the basics of working with WSGI, Apache and Django.

Deploy Django on Apache
Deploy Django on Apache

What is WSGI?

The Web Server Gateway Interface (WSGI pronounced as whiskey) describes how a web server such as Apache or Nginx communicates with web applications, and how the web applications processes or executes a request. Django primarily uses WSGI for the deployment of web applications. Django uses different WSGI servers which include:

  • Gunicorn
  • uWSGI
  • mod_wsgi

Install Apache and mod_wsgi

To install apache and mod_wsgi, execute the following commands. The mod_wsgi is used to serve the Django scripts and is specifically created for apache.

sudo yum -y update
sudo yum -y install epel-release
sudo yum -y install httpd mod_wsgi

Open port 80 for http

Although I am enabling port 80, you can use another port e.g. 8000 to access your project.

sudo firewall-cmd --zone=public --permanent --add-port=80/tcp
sudo firewall-cmd reload

Enable Software Collections

Software Collections will enable us to build, install, and use multiple versions of software on the same system, without affecting system default packages. In this case, python3. This is because CentOS 7 ships with python version 2 but our projet uses version 3. Let’s enable software collections and install python3.

sudo yum -y install centos-release-scl
sudo yum -y install rh-python36 python36-devel httpd-devel rh-python36-mod_wsgi
scl enable rh-python36 bash
python --version

Please note that Python 3.6 is set as the default Python version for this shell session. Closing the session or opening a new session from another terminal will yield Python 2.7 as the default Python version.

Prepare your project

Mostly when deploying your project to production we will use a git repository, so we will start from this scenario. Let us now clone the project and set up everything else like migrations and preparing static files.

$ cd /var/www/
$ sudo git clone <myproject>.git

Create a custom directory called logs for our specific project access and error logs from apache:

$ sudo mkdir logs

Create a virtual environment and activate it. The virtual environment will be used to make some configurations such as database migrations and static files collection:

$ virtualenv venv
$ source venv/bin/activate

We now need to install our project dependencies and requirements using pip from the requirements.txt file:

$ pip install -r requirements.txt

Let us now make migrations and server our any static files as per the Django deployment requirements on static files:

$ python manage.py makemigrations
$ python manage.py migrate
$ python manage.py collectstatic

Deactivate the virtual environment and change the project folder ownership to apache. The ownership ensures that apache can make the necessary executions, writes and reads on the project.

$ deactivate
$ sudo chown -R apache:apache /var/www/

The mod_wsgi daemon mode

On UNIX servers, it is recommended to use the daemon mode using mod_wsgi. This will create a daemon process group and delegate the Django instance to run in it. Our apache configuration file will, therefore, require us to define aWSGIDaemonProcess and WSGIProcessGroup directives.

Configure apache conf file for the project

Now configure apache conf file for our project. Please note that in this case, my project’s name is awesomedjango.

$ sudo vim /etc/httpd/conf.d/django.conf

Paste this configuration, ensure to replace your project name and path where necessary!

<VirtualHost *:80>
        ServerAdmin [email protected]
        ServerName awesomedjango.com
        DocumentRoot /var/www/

        Alias /static /var/www/awesomedjango/static
        <Directory "/var/www/awesomedjango/static">
                Options FollowSymLinks
                Order allow,deny
                Allow from all
                Require all granted
        </Directory>

        Alias /media /var/www/awesomedjango/media
        <Directory "/var/www/awesomedjango/media">
                Options FollowSymLinks
                Order allow,deny
                Allow from all
                Require all granted
        </Directory>
        ErrorLog /var/www/awesomedjango/logs/apis_error.log
        CustomLog /var/www/awesomedjango/logs/apis_access.log combined

        WSGIPassAuthorization On
        WSGIDaemonProcess awesomedjango python-path=/var/www/awesomedjango:/var/www/awesomedjango/venv/lib/python3.6/site-packages
        WSGIProcessGroup awesomedjango
        WSGIScriptAlias / /var/www/awesomedjango/v/wsgi.py

        <Directory /var/www/awesomedjango/awesomedjango>
                <Files wsgi.py>
                        Require all granted
                </Files>
        </Directory>
</VirtualHost>

If you are using a version of Apache older than 2.4, replace Require all granted with Allow from all and also add the line Order deny,allow above it.

Apache mod_wsgi specific conf for djangorestframework

If you are using djangorestframework here is a tip from them:

the authorization header is not passed through to a WSGI application by default, as it is assumed that authentication will be handled by Apache, rather than at an application level. If you are deploying to Apache, and using any non-session based authentication, you will need to explicitly configure mod_wsgi to pass the required headers through to the application. This can be done by specifying the WSGIPassAuthorization directive in the appropriate context and setting it to 'On'.

If you get the error: AttributeError: ‘module’ object has no attribute ‘lru_cache’

This error found on the apache error logs occurs when you are using python3 mod_wsgi. To rectify this execute the following:

$ sudo cp /opt/rh/httpd24/root/usr/lib64/httpd/modules/mod_rh-python36-wsgi.so /lib64/httpd/modules
$ sudo cp /opt/rh/httpd24/root/etc/httpd/conf.modules.d/10-rh-python36-wsgi.conf /etc/httpd/conf.modules.d

Restart apache server:

$ sudo systemctl restart httpd

Navigate to your domain name or IP address to view your site!. More reading.

Managing Docker Containers with Docker Compose

How To Dockerize Django Application With PostgreSQL Database

How To Dockerize a Django Application on Linux

Thanks, peace out.