The Blog of Someone Who Builds Things on the Internet

I google how to do things, then post the answers here cause I'll probably forget. Maybe someone else finds it useful.

How To Deploy A Django App to Production Using Apache

Published November 22, 2020

Here's what I'm going to assume:

  • You have a Django app running and have no issues when you python manage.py runserver
  • You are running django in a virtual environment
  • You have a Apache2 working on your server
  • You're running Ubunutu 18.04 or 20.04

Why am I writing this post when there is millions of similar posts on the internet? I ran into so many issues because 99.98% of those other posts I mentioned on the internet are stuck in the past using python2.7. It's 2020 people. Python3 was released in 2008 - get on board.

Install Dependencies

sudo apt install libapache2-mod-wsgi-py3

Honestly this was the step that caused me to write this post. Took me over an hour to find this package. Everyone was like "install libapache2-mod-wsgi" which is the WSGI module for Python2.

Configured Django

In your project directory, you should have a file named wsgi.py. I can't remember what the default version looks like, but you should have the following in it:

import os
from django.core.wsgi import get_wsgi_application
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "project_name.settings")
application = get_wsgi_application()

Make sure in place of project_name.settings you have the right project name, aka project_name_here.settings.

After you've setup the WSGI file, you need to collect static files from the various parts of your Django app. Run the following:

python manage.py collectstatic

You can read more about static files here.

Add A New Virtual Host to Apache

Make a new apache conf file, something like /etc/apache2/sites-available/001-django.conf. Go ahead and use standard template for this file, but make sure you add the following sections:

WSGIScriptAlias / /path/to/project/django/project_name/wsgi.py
WSGIDaemonProcess project-name python-home=/path/to/venv python-path=/path/to/project/django
WSGIProcessGroup project-name

Some notes on this section:

  • Next to WSGIScriptAlias you need to have / which represents the web root and then /path/to/project/django/project_name/wsgi.py which is the location of the aformentioned wsgi.py file.
  • Next to WSGIDaemonProcess you have project-name which honestly can be whatever you want, it is just a name for the daemon process name the system will assign when you boot the web server. After that, you have to list the path to the env folder in your virtual environment folder. Next to that, you have to list the path to your django project parent directory
  • Final line of this section contains the daemon process name again that you listed on line 2.
<Directory /path/to/project/django/horowitz/>
    <Files wsgi.py>
        Require all granted
    </Files>
</Directory>

Alias /static/ /path/to/project/html/static/
<Directory /path/to/project/html/static/>
    Require all granted
</Directory>

Alias /media/ /path/to/project/html/media/
<Directory /path/to/project/html/media/>
    Require all granted
</Directory>

Ok, add three Directory tags listed above. The first one gives apache access to your wsgi.py file. The second, access to static files. The third, access to user created media files. Make sure to replace the paths as needed.

Turn It On

Once your Apache configuration file is done, you need to activate it. Run

sudo a2enmod wsgi

just to make sure the previously installed module is active.

Then, run:

sudo a2ensite 001-django.conf
sudo service apache2 restart

Hopefully no error messages will be displayed, and you should be good to go! Django will be running at whatever domain you specifed in your conf file.

Full Apache2 VirtualHost file, 001-django.conf

<VirtualHost _default_:80>
    ServerName django.example.com
    ServerAdmin person@example.com

    ErrorLog /path/to/project/logs/error.log
    CustomLog /path/to/project/logs/access.log combined

    WSGIScriptAlias / /path/to/project/django/horowitz/wsgi.py
    WSGIDaemonProcess project-name python-home=/path/to/project/env python-path=/path/to/project/django
    WSGIProcessGroup project-name

    <Directory /path/to/project/django/horowitz/>
        <Files wsgi.py>
            Require all granted
        </Files>
    </Directory>

    Alias /static/ /path/to/project/html/static/
    <Directory /path/to/project/html/static/>
        Require all granted
    </Directory>

    Alias /media/ /path/to/project/html/media/
    <Directory /path/to/project/html/media/>
        Require all granted
    </Directory>

</VirtualHost>

Source 1
Source 2