Deploy Django on Apache with Virtualenv and mod_wsgi

I have recently got into Django and built a small project with it which is this blog. Then came the time for me to deploy the project on an unmanaged VPS, the process went fine yet with a few problems here and there due to the lack of a definitive guide to deployment. So I ended up checking several sources to have everything work the way I wanted. In this post, I'll be sharing the whole process with you in detail and what will hopefully be helpful to the fellow newcomers.


In this tutorial we will assume that we have a fresh Ubuntu server installation with Python already installed and nothing else. Furthermore, you should be able to have shell access to the server. If you're looking for a good and affordable VPS hosting I highly recommend Digital Ocean.

Installing Pip

Pip is a tool for installing and managing Python packages,basically a replacement for easy_install, many Python developers should be already familiar with it and have it already. If not you can install it easily via

$ sudo apt-get install python-pip python-dev build-essential

Then upgrade to latest version

$ sudo pip install --upgrade pip

The python-dev and build-essential packages are recommended to install along, because it isn't possible to install any Python module that ships with a C extension without them later on.

Setting up virtualenv

Virtualenv is, a tool for creating isolated Python environments. Virtualenv helps you create several environments that have their own installation directories for setting up private python environments and their libraries that are specific to your project and its needs. Its really a neat tool that is extremely recommended especially on your development machine for dealing with multiple Django projects and their dependencies. On top of virtualenv we have virtualenvwrapper which is basically a set of extensions that include wrappers that make it much easier to manage your virtualenvs such as creating, deleting and switching between them. So lets install virtualenvwrapper via pip, and virtualenv(its prerequisite) will get fetched and installed automatically

$ pip install virtualenvwrapper

Next we need to set the location where the virtual environments will live and the location of the script we just installed( so that we can access it later on. To do that, you'll have to add a couple of lines to your login shell startup file which is .bash_profile in this case. This file is located within your user's home directory. Open the file

$ sudo nano ~/.bash_profile

Set the following lines

export WORKON_HOME=$HOME/.virtualenvs
source /usr/local/bin/

Reload your startup file

$ source ~/.bash_profile

Now that the file is loaded we can access the script which will ease managing our virtual environments. For more information about shell startup files(.bashrc and .bash_profile) check this article

Creating a virtual environment for the project

To create a new virtual environment for our project execute

$ mkvirtualenv myprojectenv --no-site-packages

This creates a new virtual environment on the server just for our project without any inherent packages(thanks to the not-site-packages) option. myprojectenv now has just the essentials Python, and Pip. When you create a new virtualenv it will get switched to it automatically so you'll be seeing the name of your environment in the terminal as long as its loaded. To get out of your currently loaded virtualenv simply enter "deactivate". To get into a virtualenv and manage it enter "workon" followed by the name of your available virtualenv such as myprojectenv. Introducing virtualenv and virtualenvwrapper in detail is beyond the scope of this tutorial so I suggest to read more about them independantly.

Uploading your project and installing dependencies

Now that the virtual environment is ready, we're going to upload our project files to some directory on the server I will choose for this example: home/django_projects/MyProject.

Generate requirements

But before upload lets generate a requirements file for our project on the development machine, from the working environment of the project, via pip:

$ pip freeze > requirements.txt

This will generate a list of all installed libraries and their version numbers within the current working environment. You may want to edit that file in case there are unnecessary libraries listed.

Upload files

Now upload your project files to your server via ftp or scp to /home/django_projects/MyProject/

Install dependencies

After the upload navigate to your MyProject directory

$ cd /home/django_projects/MyProject/

Make sure you're working on the virtualenv of that particular project, if not

$ workon myprojectenv

Install the dependencies in requirements.txt via pip

$ pip install -r requirements.txt 

Installing and configuring Apache

Now that the project files are in place and the python requirements installed we need to set up apache to execute a .wsgi file that will launch our django application.

Install apache2 and mod_wsgi

Install apache2 and some required components

$ sudo apt-get install apache2 apache2.2-common apache2-mpm-prefork apache2-utils libexpat1

Now lets install mod_wsgi, which enables you to serve python web apps from Apache server. It is also one of the recommended ways of getting Django into production.

$ sudo apt-get install libapache2-mod-wsgi

After install make sure you restart apache for the changes to take effect

$ sudo service apache2 restart

Create a virtual host

Open up or create the virtualhost file for your domain

$ sudo nano /etc/apache2/sites-available/

Create your new virtual host node which should look something like this

<VirtualHost *:80>
    WSGIScriptAlias / var/www/

    Alias /static/ /var/www/
    <Location "/static/">
        Options -Indexes
    </Location >
</VirtualHost >

A couple of things to notice here. WSGIScriptAlias denotes the location of wsgi file that is going to get executed when our domain gets accessed.This file will have some Python code which will run our Django app as you'll see later. The other alias denotes the location of the static files for my django project. This contents of the static folder will contain all static files belonging to all the apps within my django project. The content will later be generated via the collectstatic command. You will also notice that I have put those files away from my home folder. /var/www/mydomain it just a convention which I prefer especially for static content being under the "document root".

Create the wsgi file

Now that apache points to /var/www/ we need to create that file.

sudo nano /var/www/

Your Python code in that file should be similar to this

import os
import sys
import site

# Add the site-packages of the chosen virtualenv to work with

# Add the app's directory to the PYTHONPATH

os.environ['DJANGO_SETTINGS_MODULE'] = 'myproject.settings'

# Activate your virtual env
execfile(activate_env, dict(__file__=activate_env))

import django.core.handlers.wsgi
application = django.core.handlers.wsgi.WSGIHandler()

site.addsitedir() will let you use the site packages and dependencies within the virtualenv you created initially for your specific project with your specific packages. sys.path.append() adds your project's directory to the Python path. It is advised you add both the main directory holding your apps and its child that contains the file which in this case is "myproject".

Static files

Now we need to set our static files directory and generate the static files. Navigate to your file in your project and update the following settings:

STATIC_ROOT = '/var/www/'
STATIC_URL = '/static/'

Make sure that you have created the static folder. Now within MyProject execute collectstatic

$ python collectstatic

Everything should be set now. Restart apache for the changes on your virtual host to take effect and everything should work well.

$ sudo service apache2 restart

Basically thats all what it takes to deploy a django project on a vanilla Ubuntu server. I tried to make the steps as detailed and friendly as possible, in case you spot any error please feel free to share in the comments. Also I am open to any ideas regarding better execution of anything related to this task. Definitely there are still a couple of things to discuss about this topic such as database deployment, migrations, caching, and running mod_wsgi in daemon mode(which is extremely recommended especially if you're running multiple Django projects). I'll be discussing all that in later posts so stay tuned!

Enjoyed this post? Help me spread the word and let me know your feedback!

Subscribe via