Deploying a Django App with mod_wsgi on Ubuntu 14.04

By Shaumik Daityari
We teamed up with SiteGround
To bring you the latest from the web and tried-and-true hosting, recommended for designers and developers. SitePoint Readers Get Up To 65% OFF Now

Programming in Python Django is a free, open-source, Python-based web framework. Django follows the MVC architectural pattern, with special emphasis on creating your applications rapidly. In recent times, Django has become a popular choice for creating web applications. Popular services like Instagram, Bitbucket and Pinterest were developed using Django.

In development mode, Django has a development server, which is sufficient for testing purposes. Once you complete a web application and it's ready for production, the process of setting up the application on a server might be overwhelming for some, especially if you're doing it for the first time. This article provides a step-by-step guide on how to deploy Django-based web applications using mod_wsgi.


WSGI, or Web Server Gateway Interface, is a Python standard for web servers. Python was traditionally developed as a programming language, so WSGI provides a way for web servers to serve applications developed in Python. It enables web applications in Python to interact with web servers, acting as a link between the two.

Apache is one of the most popular web servers, and mod_wsgi is an Apache module that's used to host Python applications on Apache. It's also a relatively simple way of deploying a Django application.

Python comes installed by default in Ubuntu 14.04. Let us now look at the step by step guide to deploy a Django application using mod_wsgi.

1. Creating a Django Application

the file In this section, we're going to install the required packages and set up a hello world Django application to be served by mod_wsgi. We'll assume that you've logged in to a newly created virtual machine.

1.1 Create a New User (Optional)

If you create a VM using AWS or Microsoft Azure, you're logged in as a user that you specified while creating the VM, so you can skip this step.

There are some extra steps if you're first logged in as a root user (if you create a new VM with Digital Ocean). Although you can perform all functions using the same user, it's generally advised to create a new user. Here are detailed instructions for creating users and adding them to the sudoers list on Ubuntu 14.04.

1.2 Install a Python Package Manager

In this tutorial, we're going to use the Ubuntu package manager, apt-get, for installing packages. However, on a fresh VM, you must update packages first by running the following command:

sudo apt-get update

Pip is a Python package manager that helps us install, modify or remove Python packages. The easiest way to install pip in Ubuntu is by using the Ubuntu package manager apt-get:

sudo apt-get install python-pip

apt-get installs the latest stable version of pip. Alternatively, if you require a specific version of pip, you can install it from the source code. However, for the purposes of deploying a Django application, installing it through the package manager should suffice.

You can also use easy_install as an alternative to pip. However, in this tutorial, we'll use pip to install packages.

1.3 Install Django

If you're creating a project form scratch, you just need the Django package. In this example, we wouldn't require any further packages:

sudo pip install Django

If you want to install a specific version of the package, you can specify it in the command as shown below (in case your application was coded in an older version of Django):

sudo pip install Django==1.5.5

You can also install Django through the package manager apt-get. Caution must be exercised when following this step as apt-get might not be updated with the latest stable version as compared to pip.

1.4 Install and Freeze Other Requirements (Optional)

If you're deploying an existing project, you can recursively run pip to install the dependencies in the project. Generally, there's a file requirements.txt in the source directory of the project, which contains the packages required to run the project:

pip install -r requirements.txt

If your system has other Python projects, the versions of different Python packages might interfere with one another. A solution to this is to use virtualenv and keep every project in its own virtual Python environment. Here's a tutorial on getting started with virtualenv. Since we're deploying an application on a server, we aren't going to work with virtualenv in this tutorial.

If you're working on a Django application and you want to create or update the requirements file, you could just run the following:

pip freeze > requirements.txt

pip freeze prints a list of installed Python packages in your current environment, and the > stores the output of the command pip freeze into the file requirements.txt.

1.5 Create Hello World Application

To create a new project in Django named helloworld, run the following: startproject helloworld

You'll notice that a new directory helloworld has been created. Change your directory to helloworld and run the following to start a new app helloapp within the Django project:

cd helloworld/ startapp helloapp

We can now create a sample view that prints Hello World in our browser. First, add the new app to your project's Search for INSTALLED_APPS in the file, and add the name of our new app to it. It should look something like the following:


Next, we add a URL pattern to the of the project. It looks something like this:

urlpatterns = patterns('',
    # Examples:
    # url(r'^$', 'helloworld.views.home', name='home'),
    url(r'^', 'helloapp.views.home_view'),

This instructs Django to look for the function home_view within the of the app helloapp. Therefore, we edit the file to look like the following:

from django.http import HttpResponse

def home_view(request):
    return HttpResponse('Hello World')

Next, we run the development server by running the following:

python runserver

The code for the hello world Django application is available on GitHub.

2. Serving a Django Application Through Apache and mod_wsgi

Django file structure Now that we've created a hello world application, this section explores how you can configure different settings in order to serve the application through Apache and mod_wsgi.

2.1 Install Apache2

Using the apt-get command, installing Apache is also a one step process. Run the following command:

sudo apt-get install apache2

2.2 Install mod_wsgi

The mod_wsgi module for Apache can be installed on Ubuntu 14.04 using apt-get:

sudo apt-get install libapache2-mod-wsgi

If you're using Python 3 instead of Python 2, run the following:

sudo apt-get install libapache2-mod-wsgi-py3

Here's a tutorial with detailed instructions on installing mod_wsgi on Ubuntu.

2.3 Modifying Directory Structure

To serve the Django application through mod_wsgi, we need to write a WSGI script that serves as a connection between Apache and Django. The Django file structure by default is something like this:


We're going to change it a bit and add an apache directory within mysite to contain three files:


This helps separate the logic, and you could also ignore the directory as a whole in your version control system.

Note: if you're using a version control software (VCS) like Git, you can add the apache directory to the ignore list of the VCS.

2.4 Create WSGI Script

The empty file tells Python to treat the directory as a package. The imports all settings and overrides any settings for production. For instance, databases and debug settings for production might be different from those of development, and you may want to separate them from the source code:


from mysite.settings import *

DEBUG = True
ALLOWED_HOSTS = ['', '']

Finally, the file contains the WSGI settings. We're assuming that the root directory shown above is contained in the home directory of the user (/home/myuser/):
import os, sys
# Calculate the path based on the location of the WSGI script.
apache_configuration= os.path.dirname(__file__)
project = os.path.dirname(apache_configuration)
workspace = os.path.dirname(project)

# Add the path to 3rd party django application and to django itself.
os.environ['DJANGO_SETTINGS_MODULE'] = 'mysite.apache.override'
import django.core.handlers.wsgi
application = django.core.handlers.wsgi.WSGIHandler()

You also need to transfer the ownership of the apache directory to Apache's default user www-data in order to allow it to access the directory:

sudo chown www-data:www-data apache/

Note: Thanks to Scott Taggart in the comments for pointing out that
the script shown above will throw an error in Django 1.8 (Django 1.7 was used for this article). As noted by Scott, for the script to work with Django 1.8, the last two lines need to be replaced with:

from django.core.wsgi import get_wsgi_application
application = get_wsgi_application()

2.5 Configure Apache Settings

To configure Apache to use your WSGI script, you need to edit the configuration file as shown below (using a text editor, in this case VIM):

sudo vi /etc/apache2/sites-enabled/000-default.conf

Add the following lines to the file:

<VirtualHost *:80>
    WSGIScriptAlias /mypath/ /home/myuser/mysite/apache/
    <Directory "/home/myuser/mysite/apache/">
      Require all granted

The first line adds an alias of /mypath to the root of your web application. Your web application would now run on your domain— Replace /mypath/ above with / if you want your domain, , to directly point to the Django application. The <Directory> block is to allow requests to the directory that contains the WSGI script.

If you have a custom robots.txt and favicon, you may add an alias as follows:

Alias /robots.txt /home/myuser/mysite/robots.txt
Alias /favicon.ico /home/myuser/mysite/favicon.ico

In each of the lines above, the first argument after the keyword Alias signifies the URL pattern, and the second argument shows the path to the file to be served. This example assumes that your files robots.txt and favicon.ico are saved in the mysite directory.

To serve static and media files, you need to create their alias entries separately:

Alias /media/ /home/myuser/mysite/media/
Alias /static/ /home/myuser/mysite/static/

<Directory /path/to/>
Require all granted

<Directory /path/to/>
Require all granted

Finally, save and close the file and restart Apache to see the changes:

sudo service apache2 restart

Note for older versions of Apache

For Apache versions older than 2.4 (like the one in Ubuntu 12.04), you need to replace Require all granted by Allow from all after adding the line Order deny,allow above it.

Note for static files being served by Django packages

Some Django packages have their own static and media files. In the development version, it's taken care of by Django, but it doesn't work that way when serving through Apache (including the Django admin static files). The static files are usually located in the same place where packages are installed.

An easy way to override them is to copy their static files to your static directory (and commit them), which is a rather messy solution. A better way would be to create an alias for the particular set of static files, just like you created alias entries for static and media roots.

A discussion on StackOverflow gives an example of how to take care of the Django admin static and media files. You need to follow the same pattern for every other package that uses static and media files.


In recent years, Django has become the first choice among many programmers. Although many accuse Python of being slow, websites like Instagram and Disqus—which run on Django—have scaled to millions of users. If you're interested, you may want to read how HackerEarth scaled their Django-based web application using mod_wsgi.

I hope this tutorial has helped you in deploying your Django-based web application on the server using Apache and mod_wsgi. If you faced any difficulties, let me know in the comments below.

We teamed up with SiteGround
To bring you the latest from the web and tried-and-true hosting, recommended for designers and developers. SitePoint Readers Get Up To 65% OFF Now
  • Machozi

    Great post! Sitepoint has never had a lot of love for Python/Django. I hope this will be the beginning of a shift.

  • amy acker

    Ubuntu install images have a secure boot signed bootloader, so it’s supposed to Just Work. :)

  • Hi Scott,

    Could you let me know which version of Django you are using? It’s probably behaving differently because you and I used different versions of Django.

    • scott taggart

      sorry, I should have mentioned that: 1.8

      • Okay, this worked with 1.7. I think I should mention an edit here. Thanks for pointing it out :)

      • Ralph Mason

        Thanks Scott. A note has been added to the article!

  • Moshfiqur Rahman

    You are right. I am using Django 1.9 and still got the same problem. Your trick solved the problem. Thanks :)

  • Anurag Priyadarshi

    very organised and clear instructions. Thanks

  • Rakibul hassan Rakib

    In this line url(r’^’, ‘helloapp.views.home_view’), will be url(r’^’, helloapp.views.home_view),

  • Vishnu Iyer

    @sdaityari:disqus Hey I tried your steps .but i am getting an internal server 500 error. I am not able to figure it out. Can you help me out. I am trying it in an aws ec2 ubuntu server instance.Can i get your skype or google+ id so that I communicate properly. Its Urgent.Thank you.

    • Ralph Mason

      Hey @Vishnu, I recommend you post your question on the SitePoint forums here, linking to this post and pinging @sdaityari.